[CodeVS 1217]借教室
很容易想到线段树水过。
很可惜,这样估计也就70分。
然后这个问题是符合单调性的……可以二分答案哒。不过怎么判定?
不知道诸位还记得差分序列?记得就好,用差分序列的方法维护即可。
等等,我怎么感觉你这个做法是[tex]O(nlogn)[/tex]的,能过?
推荐一个行之有效的优化:注意一下每一次修改都是在之前的基础上反悔或前进几次,所以就可以直接在原基础上修改差分序列辣~这样效率是很高滴~
代码:
#include <cstdio> const int maxn=1e6+5; typedef long long ll; ll A[maxn]; int l[maxn],r[maxn]; ll d[maxn]; ll C[maxn]; int n,m,last=0; bool Check(int x){ register int i,cnt=0; if(x>last) for(i=last+1;i<=x;i++){ C[l[i]]+=d[i]; C[r[i]+1]-=d[i]; } if(x<last) for(i=x+1;i<=last;i++){ C[l[i]]-=d[i]; C[r[i]+1]+=d[i]; } last=x; for(i=1;i<=n;i++){ cnt+=C[i]; if(cnt>A[i]) return false; } return true; } int main(){ register int i,L,M,R; scanf("%d%d",&n,&m); for(i=1;i<=n;i++){ scanf("%lld",&A[i]); } for(i=1;i<=m;i++){ scanf("%lld%d%d",&d[i],&l[i],&r[i]); } L=1; R=m+1; while(L<R){ M=L+(R-L)/2; if(Check(M)){ L=M+1; }else{ R=M; } } if(L==m+1) puts("0"); else printf("-1\n%d\n",L); return 0; }
[CodeVS 3729]飞扬的小鸟
论如何把一个完全背包出的高大上……
这个题啊……细节特别多……
首先,不建议开滚动数组,细节处理就会疯。
然后呢……真的不太好说了……还是看代码吧(尽管代码还是有明显的滚动数组痕迹)。
代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=10005,maxm=1005; const int INF=0x7f7f7f7f; #define REP(i,n) for(i=0;i<(n);i++) #define REP_B(i,n) for(i=1;i<=(n);i++) int GZ[maxn][2]; bool haveGZ[maxn]; int A[maxn][2]; int d[maxn][maxm]; int main(){ int n,m,k,temp; register int i,j,v,now,pre,ans=INF,cnt; scanf("%d%d%d",&n,&m,&k); cnt=k; REP(i,n){ scanf("%d%d",&A[i][0],&A[i][1]); } for(i=0;i<=n;i++){ GZ[i][0]=0; GZ[i][1]=m+1; } REP_B(i,k){ scanf("%d",&temp); haveGZ[temp]=true; scanf("%d%d",&GZ[temp][0],&GZ[temp][1]); } memset(d,0x7f,sizeof(d)); memset(d[0],0,sizeof(d[0])); d[0][0]=INF; REP_B(i,n){ now=i; pre=i-1; d[now][0]=INF; int& X=A[i-1][0]; int& Y=A[i-1][1]; REP_B(j,m){ if(j-X>0 && d[pre][j-X]<INF) d[now][j]=min(d[now][j],d[pre][j-X]+1); if(j-X>0 && d[now][j-X]<INF) d[now][j]=min(d[now][j],d[now][j-X]+1); if(j==m) for(v=j-X;v<=m;v++){ if(d[pre][v]<INF) d[now][j]=min(d[now][j],d[pre][v]+1); if(d[now][v]<INF) d[now][j]=min(d[now][j],d[now][v]+1); } } for(j=GZ[i][0]+1;j<GZ[i][1];j++){ if(j+Y<=m && d[pre][j+Y]<INF) d[now][j]=min(d[now][j],d[pre][j+Y]); } if(haveGZ[i]){ REP_B(j,GZ[i][0]) if(d[now][j]<INF){ d[now][j]=INF; } for(j=GZ[i][1];j<=m;j++) if(d[now][j]<INF){ d[now][j]=INF; } } } for(i=n;i>=1;i--){ for(j=m;j>=1;j--){ ans=min(ans,d[i][j]); } if(ans<INF) break; if(haveGZ[i]) cnt--; } if(cnt==k){ printf("1\n%d\n",ans); }else{ printf("0\n%d\n",cnt); } return 0; }
[BZOJ 1050]旅行/[CodeVS 1001]舒适的路线
这两个题是一样的啊……
让路径上最大值最小当然要玩瓶颈路,瓶颈路需要MST,然后Kruskal求MST时最小边不是不可控的!也就是说我们可以控制最小边,从而求出符合要求的生成树,然后凿出瓶颈路。
所以我们可以直接枚举最小边,然后Kruskal求生成树,之后求瓶颈路。至于是否有解,第一遍Kruskal(第一遍求的其实是MST)之后看看是否联通即可。
不过这个题求瓶颈路个人建议用BFS而不是DFS,且不谈BFS效率高还不易炸堆栈段(尽管这个题没有炸堆栈段的隐患),DFS本身细节就很多,容易错。
代码:
/************************************************************** Problem: 1050 User: danihao123 Language: C++ Result: Accepted Time:3588 ms Memory:1172 kb ****************************************************************/ #include <cstdio> #include <cctype> #include <algorithm> #include <cstring> #include <utility> #include <bitset> #include <vector> #include <queue> using namespace std; const int maxn=501,maxm=10001; #define REP(i,n) for(i=0;i<(n);i++) #define REPB(i,n) for(i=1;i<=(n);i++) #define CUS_REP(i,u,v) for(i=(u);i<(v);i++) #define REP_G(i,u) for(i=first[u];i;i=next[i]) #define CL_ARR(x,v) memset(x,v,sizeof(x)) typedef pair<int,int> pii; int n; int S,T; // Graph int first[maxn]; int next[maxm],to[maxm],dist[maxm]; int G_cnt=0; inline void Add_Edge(int u,int v,int d){ G_cnt++; next[G_cnt]=first[u]; first[u]=G_cnt; to[G_cnt]=v; dist[G_cnt]=d; } inline void Clear_Graph(){ CL_ARR(first,0); CL_ARR(next,0); CL_ARR(to,0); CL_ARR(dist,0); G_cnt=0; } // BFS bitset<maxn> vis; struct Node{ int x,maxv,minv; }; pii BFS(){ register int a,b,i; queue<Node> Q; Node tt; Q.push((Node){S,-1,0x7fffffff}); vis[S]=true; while(!Q.empty()){ tt=Q.front(); Q.pop(); if(tt.x==T) return make_pair(tt.maxv,tt.minv); REP_G(i,tt.x){ if(!vis[to[i]]){ vis[to[i]]=true; Q.push((Node){to[i],max(tt.maxv,dist[i]),min(tt.minv,dist[i])}); } } } } // BINGCHA Set int p[maxn],rank[maxn]; int find_set(int x){ if(p[x]==x) return x; else return p[x]=find_set(p[x]); } void link_set(int x,int y){ if(rank[x]>rank[y]){ p[y]=x; }else{ p[x]=y; if(rank[x]==rank[y]) rank[y]++; } } inline void union_set(int x,int y){ link_set(find_set(x),find_set(y)); } inline bool is_same(int x,int y){ return find_set(x)==find_set(y); } inline void init_set(){ register int i; for(i=1;i<=n;i++) p[i]=i; CL_ARR(rank,0); } // MST struct Edge{ int u,v,d; bool operator <(const Edge& x) const{ return d<x.d; } }; vector<Edge> E; bool OK=false; pii ans; void MST(int x){ register int i,a,b,cnt=0; pii que; register double jia,yi; Clear_Graph(); init_set(); CUS_REP(i,x,E.size()){ if(!is_same(E[i].u,E[i].v)){ Add_Edge(E[i].u,E[i].v,E[i].d); Add_Edge(E[i].v,E[i].u,E[i].d); cnt++; union_set(E[i].u,E[i].v); if(cnt==n-1) break; } } if(is_same(S,T)){ OK=true; }else{ return; } vis.reset(); que=BFS(); a=que.first; b=que.second; jia=((double)a)/((double)b); yi=(double(ans.first))/(double(ans.second)); if(jia<yi){ ans.first=a; ans.second=b; } } int gcd(int a,int b){ return b==0?a:gcd(b,a%b); } // I/O优化 inline int readint(){ char c=getchar(); register int x=0; while(!isdigit(c)) c=getchar(); while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); } return x; } int main(){ int m; register int i,u,v,d; n=readint(); m=readint(); REP(i,m){ u=readint(); v=readint(); d=readint(); E.push_back((Edge){u,v,d}); } S=readint(); T=readint(); sort(E.begin(),E.end()); ans.first=0x7fffffff; ans.second=1; REP(i,m){ MST(i); if(!OK){ if(!i){ puts("IMPOSSIBLE"); return 0; }else{ break; } } } d=gcd(ans.first,ans.second); ans.first/=d; ans.second/=d; if(ans.second==1) printf("%d\n",ans.first); else printf("%d/%d\n",ans.first,ans.second); return 0; }
[CodeVS 3269]混合背包
哎……现在才敢说真正会背包DP……
这个题可以分成三类问题分别处理,然后用一个一维数组一起递推。
0-1背包和完全背包都很简单。多重背包直接递推的话复杂度很高,可以考虑单调队列优化……然而窝太弱……不过还是用了
代码:
#include <cstdio> #include <algorithm> #include <queue> #include <deque> using namespace std; const int maxn=201,maxv=200001; struct DQueue{ deque<int> D; queue<int> Q; void push(int x){ Q.push(x); while(!D.empty() && x>D.back()) D.pop_back(); D.push_back(x); } int top(){ return D.front(); } void pop(){ if(D.front()==Q.front()) D.pop_front(); Q.pop(); } int size(){ return Q.size(); } void clear(){ while(!Q.empty()) Q.pop(); D.clear(); } }; int d[maxv]; int v; void pack(int V,int W,int c){ register int i,j,m; if(c==1){ for(i=v;i>=V;i--) d[i]=max(d[i],d[i-V]+W); }else{ if(c==-1){ for(i=V;i<=v;i++) d[i]=max(d[i],d[i-V]+W); }else{ c=min(c,v/V); for(i=0;i<V;i++){ m=(v-i)/V; DQueue Q; for(j=0;j<=m;j++){ if(Q.size()==c+1) Q.pop(); Q.push(d[j*V+i]-j*W); d[j*V+i]=Q.top()+j*W; } } } } } int main(){ register int i; int n,x,y,z; scanf("%d%d",&n,&v); for(i=1;i<=n;i++){ scanf("%d%d%d",&x,&y,&z); pack(x,y,z); } printf("%d\n",d[v]); return 0; }
[CodeVS 1044]拦截导弹
第一问很显然是最长不升子序列,直接DP即可。
第二问咋整?暴力?网络流?
其实就是最长不降子序列。具体证明嘛……自己找吧。
代码:
#include <cstdio> #include <algorithm> using namespace std; const int maxn=22; int d[maxn],f[maxn]; int A[maxn]; int main(){ int n; register int i,j,ans=0; for(n=1;;n++) if(scanf("%d",&A[n])!=1) break; n--; for(i=1;i<=n;i++){ d[i]=1; for(j=i-1;j>=1;j--) if(A[j]>=A[i]) d[i]=max(d[i],d[j]+1); ans=max(ans,d[i]); } printf("%d\n",ans); ans=0; for(i=1;i<=n;i++){ f[i]=1; for(j=i-1;j>=1;j--) if(A[j]<=A[i]) f[i]=max(f[i],f[j]+1); ans=max(ans,f[i]); } printf("%d\n",ans); return 0; }
[CodeVS 1012]最大公约数和最小公倍数问题
很经典的问题了吧……然而现在才A……
应注意[tex]P*Q=x*y[/tex],然而[tex]P[/tex]和[tex]Q[/tex]都可表示为[tex]x[/tex]的乘积,问题就好思考多了。答案就是[tex]y/x[/tex]的质因子的子集数(同一种质因子不能同时分配到P与Q中,否则gcd(P,Q)会不等于x)。
注意有可能无解!
代码:
#include <iostream> using namespace std; inline int fj(int x){ register int i,ans=0; for(i=2;i<=x && x>1;i++) if(!(x%i)){ ans++; while(!(x%i)) x/=i; } return ans; } int main(){ int a,b; register int ans; cin>>a>>b; if(b%a) ans=0; else ans=a==1?1:1<<fj(b/a); cout<<ans<<endl; return 0; }
[CodeVS 3289]花匠
这破题吃枣药丸……
这题略加思索,就能发现最优策略是找转折点。但需要注意相等连块中也会存在转折点……并且,n<3时不要搬走花!
代码:
#include <cstdio> const int maxn=100001; int A[maxn]; int main(){ int n; bool flag=false,tal; register int i,ans=0; scanf("%d",&n); if(n<3){ printf("%d\n",n); return 0; } for(i=1;i<=n;i++){ scanf("%d",&A[i]); } for(i=1;i<=n;i++){ if(i==1){ ans++; continue; } if(A[i]!=A[i-1]) flag=true; if(i==n){ if(flag) ans++; break; } if(A[i]==A[i-1] && i>=2) A[i-1]=A[i-2]; if((A[i]>A[i-1] && A[i]>A[i+1]) || (A[i]<A[i-1] && A[i]<A[i+1])) ans++; } printf("%d\n",ans); return 0; }