我校是神校,作业竟然选自POJ,难道不知道“珍爱生命 勿刷POJ”么?
所有注明模板题的我都十分傲娇地没有打,于是只打了6道题(其实模板题以前应该打过一部分但懒得找)(不过感觉我模板还是不够溜要找个时间刷一发)。
没注明模板题的都是傻逼题,其实也是模板题。
题目大致按照傻逼程度从大到小排序。
POJ 3264 Balanced Lineup
题意:n个数,m个询问,每次问max[l,r]-min[l,r]。
题解:这道题竟然没标注模板题真是惊讶...

1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int maxn=5e4+5; 5 6 int g[maxn][16],h[maxn][16]; 7 int n,m; 8 9 void get(){ 10 for(int j=1;(1<<j)<=n;j++) 11 for(int i=1;i+(1<<j)-1<=n;i++) 12 g[i][j]=min(g[i][j-1],g[i+(1<<(j-1))][j-1]), 13 h[i][j]=max(h[i][j-1],h[i+(1<<(j-1))][j-1]); 14 } 15 16 int ask(int l,int r){ 17 int k=0; 18 while(1<<(k+1)<=r-l+1) k++; 19 int Min=min(g[l][k],g[r-(1<<k)+1][k]); 20 int Max=max(h[l][k],h[r-(1<<k)+1][k]); 21 return Max-Min; 22 } 23 24 int main(){ 25 scanf("%d%d",&n,&m); 26 for(int i=1;i<=n;i++){ 27 scanf("%d",&h[i][0]); 28 g[i][0]=h[i][0]; 29 } 30 get(); 31 32 int l,r; 33 for(int i=1;i<=m;i++){ 34 scanf("%d%d",&l,&r); 35 printf("%d ",ask(l,r)); 36 } 37 return 0; 38 }
POJ 3368 Frequent values
题意:n个数组成一个不降序列,m个询问,每次问[l,r]出现次数最多的数。
题解:预处理把相同的数变成一块,查询时二分lr所在块,这部分求RMQ,lr不在块内的单独处理。
这种划分块的细节及边界一定要想清了,手一抖就会错。

1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 const int maxn=1e5+5; 6 7 int a[maxn],b[maxn],c[maxn],t[maxn],tot; 8 int h[maxn][17]; 9 int n,m; 10 11 void clear(){ 12 memset(h,127,sizeof(h)); 13 memset(a,0,sizeof(a)); 14 memset(b,0,sizeof(b)); 15 memset(c,0,sizeof(c)); 16 memset(t,0,sizeof(t)); 17 tot=0; 18 } 19 20 void get(){ 21 for(int i=1;i<=tot;i++) h[i][0]=t[i]; 22 for(int j=1;(1<<j)<=tot;j++) 23 for(int i=1;i+(1<<j)-1<=tot;i++) 24 h[i][j]=max(h[i][j-1],h[i+(1<<(j-1))][j-1]); 25 } 26 27 int ask(int l,int r){ 28 if(l>r) return 0; 29 int k=0; 30 while(1<<(k+1)<=r-l+1) k++; 31 return max(h[l][k],h[r-(1<<k)+1][k]); 32 } 33 34 int main(){ 35 36 while(scanf("%d%d",&n,&m)!=EOF){ 37 if(n==0) break; 38 clear(); 39 40 for(int i=1;i<=n;i++){ 41 scanf("%d",&a[i]); 42 if(i==1||a[i]!=a[i-1]){ 43 ++tot; 44 b[tot]=i; 45 t[tot]=1; 46 c[tot-1]=i-1; 47 } 48 else t[tot]++; 49 } 50 c[tot]=n; 51 b[tot+1]=n+1; 52 get(); 53 54 int ll,rr,l,r; 55 for(int i=1;i<=m;i++){ 56 scanf("%d%d",&ll,&rr); 57 l=lower_bound(b+1,b+tot+1,ll)-b; 58 r=upper_bound(c+1,c+tot+1,rr)-c-1; 59 if(r==l-2){ 60 printf("%d ",rr-ll+1); 61 continue; 62 } 63 int ans=max(b[l]-ll,rr-c[r]); 64 ans=max(ans,ask(l,r)); 65 printf("%d ",ans); 66 } 67 } 68 }
POJ 2763 Housewife Wind
题意:n个节点的树,两个操作,一询问(u,v)距离,二修改某条边边权。
题解:树上的路径长度,很快反应dist[u]+dist[v]-2*dist[lca],这个就像前缀和表示序列一样。
修改一条边对单点到跟链的影响很好统计,利用dfs序+树状数组即可。

1 #include<cstdio> 2 #include<algorithm> 3 #define ll long long 4 using namespace std; 5 const int maxn=1e5+5,POW=18; 6 7 int p[maxn][POW],pw[maxn],que[maxn],idx[maxn],l[maxn],r[maxn],dep[maxn]; 8 int e[maxn*2],w[maxn*2],nxt[maxn*2],head[maxn],tot; 9 ll d[maxn],c[maxn]; 10 int n,m,s,clock; 11 12 void dfs(int u,ll dist){ 13 ++clock; 14 que[clock]=u; 15 l[u]=clock; 16 d[u]=dist; 17 dep[u]=dep[p[u][0]]+1; 18 19 for(int i=1;i<POW;i++) p[u][i]=p[p[u][i-1]][i-1]; 20 21 for(int i=head[u];i;i=nxt[i]){ 22 int v=e[i]; 23 if(v==p[u][0]) continue; 24 p[v][0]=u,pw[v]=w[i]; 25 dfs(v,dist+w[i]); 26 } 27 r[u]=clock; 28 } 29 30 int LCA(int u,int v){ 31 if(dep[u]>dep[v]) swap(u,v); 32 if(dep[u]<dep[v]){ 33 int del=dep[v]-dep[u]; 34 for(int i=0;i<POW&&del;i++) 35 if(del&(1<<i)) v=p[v][i]; 36 } 37 if(u==v) return u; 38 for(int i=POW-1;i>=0;i--) 39 if(p[u][i]!=p[v][i]) u=p[u][i],v=p[v][i]; 40 u=p[u][0]; 41 return u; 42 } 43 44 void add(int x,int w){ 45 for(int i=x;i>0;i-=(i&-i)) c[i]+=w; 46 } 47 ll sum(int x){ 48 ll ret=0; 49 for(int i=x;i<=n;i+=(i&-i)) ret+=c[i]; 50 return ret; 51 } 52 53 void adde(int u,int v,int g){ 54 ++tot; 55 e[tot]=v,w[tot]=g; 56 nxt[tot]=head[u]; 57 head[u]=tot; 58 } 59 60 int main(){ 61 scanf("%d%d%d",&n,&m,&s); 62 int u,v,g; 63 for(int i=1;i<n;i++){ 64 scanf("%d%d%d",&u,&v,&g); 65 adde(u,v,g); 66 adde(v,u,g); 67 } 68 69 dfs(1,0); 70 for(int i=1;i<=n;i++) idx[que[i]]=i; 71 72 int x,num; 73 for(int i=1;i<=m;i++){ 74 scanf("%d",&x); 75 if(x==0){ 76 scanf("%d",&u); 77 int lca=LCA(s,u); 78 ll ans=d[u]+d[s]-2*d[lca]; 79 ans+=(sum(idx[u])+sum(idx[s])-2*sum(idx[lca])); 80 printf("%lld ",ans); 81 s=u; 82 } 83 else{ 84 scanf("%d%d",&num,&g); 85 u=e[num*2-1],v=e[num*2]; 86 if(p[v][0]!=u) swap(u,v); 87 add(l[v]-1,pw[v]-g); 88 add(r[v],-pw[v]+g); 89 pw[v]=g; 90 } 91 } 92 return 0; 93 }
POJ 3728 The merchant
题意:n个节点带权的树,m个询问,问从x->y的路径上max-min为多少,且路径上max位置必须在min位置之前。
题解:分治的思想,我们怎么合并左右两段路?①maxmin全在路1;②全在路2;③max在路1,min在路二。
保存四个量up[x],down[x],min[x],max[x]。
分别代表x->p最优解,p->x最优解,x-p路径上最小值,x-p路径上最大值。
当然还是要用到LCA,如果用trajan就在并查集合并时处理,倍增就更好处理了。
tarjan要想清,要注意答案在lca处统计,下次再码一发倍增。
听说其他同学写了180+行,十分好奇写了些什么>.<

1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int maxn=5e4+5; 5 6 int up[maxn],down[maxn],Min[maxn],Max[maxn]; 7 int n,m; 8 9 int heade[maxn],e[maxn*2],nxte[maxn*2]; 10 void adde(int u,int v,int k){ 11 e[k]=v,nxte[k]=heade[u]; 12 heade[u]=k; 13 } 14 15 int headq[maxn],q[maxn*2],nxtq[maxn*2]; 16 void addq(int u,int v,int k){ 17 q[k]=v,nxtq[k]=headq[u]; 18 headq[u]=k; 19 } 20 21 int heada[maxn],a[maxn*2],nxta[maxn*2],tot; 22 void adda(int u,int num){ 23 ++tot; 24 a[tot]=num,nxta[tot]=heada[u]; 25 heada[u]=tot; 26 } 27 28 int p[maxn],vis[maxn],ans[maxn]; 29 void find(int x){ 30 if(p[x]==x) return; 31 find(p[x]); 32 up[x]=max(up[x],max(up[p[x]],Max[p[x]]-Min[x])); 33 down[x]=max(down[x],max(down[p[x]],Max[x]-Min[p[x]])); 34 Min[x]=min(Min[x],Min[p[x]]); 35 Max[x]=max(Max[x],Max[p[x]]); 36 p[x]=p[p[x]]; 37 } 38 39 void LCA(int u){ 40 vis[u]=1; 41 for(int i=headq[u];i;i=nxtq[i]) 42 if(vis[q[i]]){ 43 int v=q[i]; 44 find(v); 45 int lca=p[v]; 46 adda(lca,i); 47 } 48 49 for(int i=heade[u];i;i=nxte[i]) 50 if(!vis[e[i]]){ 51 int v=e[i]; 52 LCA(v); 53 p[v]=u; 54 } 55 56 for(int i=heada[u];i;i=nxta[i]){ 57 int k=a[i],x,y; 58 if(k%2==1) x=q[k+1],y=q[k]; 59 else x=q[k],y=q[k-1]; 60 find(x),find(y); 61 k=(k+1)/2; 62 ans[k]=max(up[x],max(down[y],Max[y]-Min[x])); 63 } 64 } 65 66 int main(){ 67 scanf("%d",&n); 68 for(int i=1;i<=n;i++){ 69 scanf("%d",&Min[i]); 70 Max[i]=Min[i]; 71 p[i]=i; 72 } 73 74 int u,v; 75 for(int i=1;i<n;i++){ 76 scanf("%d%d",&u,&v); 77 adde(u,v,i*2-1); 78 adde(v,u,i*2); 79 } 80 81 scanf("%d",&m); 82 for(int i=1;i<=m;i++){ 83 scanf("%d%d",&u,&v); 84 addq(u,v,i*2-1); 85 addq(v,u,i*2); 86 } 87 88 LCA(1); 89 for(int i=1;i<=m;i++) 90 printf("%d ",ans[i]); 91 92 return 0; 93 }
POJ 3417 Network
题意:n个节点的树,加入m条新边,现在删除一条旧边一条新边使得新的到的图不再联通,求方案数。
题解:乍一看没什么思路,画个图就很好想了。
加入(u,v)这条边,会形成一个u-v-lca的环。
考虑每一条旧边,如果所在环个数为0,那么另一条边怎么选都可以,贡献为m;如果为1,那么必须删除使它形成环的新边,贡献为1;如果>1,弃疗,贡献为0;
然后是常用技巧,打标记然后树形dp一遍。
f[u]++; f[v]++; f[lca]-=2;
对于统计边是这样,如果是统计点就是f[lca]--, f[p[lca]]--, 如Bzoj 松鼠的新家。

1 #include<cstdio> 2 #include<vector> 3 using namespace std; 4 const int maxn=1e5+5; 5 6 int vis[maxn],f[maxn],p[maxn]; 7 int e[maxn*2],nxte[maxn*2],heade[maxn],tote; 8 int q[maxn*2],nxtq[maxn*2],headq[maxn],totq; 9 int n,m; 10 11 int find(int x){return p[x]==x?x:p[x]=find(p[x]);} 12 13 void adde(int u,int v){ 14 tote++; 15 e[tote]=v; 16 nxte[tote]=heade[u]; 17 heade[u]=tote; 18 } 19 20 void addq(int u,int v){ 21 totq++; 22 q[totq]=v; 23 nxtq[totq]=headq[u]; 24 headq[u]=totq; 25 } 26 27 void tarjan(int u){ 28 vis[u]=1; 29 for(int i=headq[u];i;i=nxtq[i]) 30 if(vis[q[i]]){ 31 int v=q[i],lca=find(v); 32 f[u]++,f[v]++; 33 f[lca]-=2; 34 } 35 36 for(int i=heade[u];i;i=nxte[i]) 37 if(!vis[e[i]]){ 38 int v=e[i]; 39 tarjan(v); 40 p[v]=u; 41 } 42 } 43 44 void dfs(int fa,int u){ 45 for(int i=heade[u];i;i=nxte[i]){ 46 int v=e[i]; 47 if(v==fa) continue; 48 dfs(u,v); 49 f[u]+=f[v]; 50 } 51 } 52 53 int main(){ 54 scanf("%d%d",&n,&m); 55 for(int i=1;i<=n;i++) p[i]=i; 56 57 int u,v; 58 for(int i=1;i<n;i++){ 59 scanf("%d%d",&u,&v); 60 adde(u,v),adde(v,u); 61 } 62 63 for(int i=1;i<=m;i++){ 64 scanf("%d%d",&u,&v); 65 addq(u,v),addq(v,u); 66 } 67 68 tarjan(1); 69 dfs(0,1); 70 71 long long ans=0; 72 for(int i=2;i<=n;i++) 73 if(f[i]==1) ans++; 74 else if(!f[i]) ans+=m; 75 76 printf("%lld ",ans); 77 return 0; 78 }
POJ 2374 Fence Obstacle Course
题意:不好描述自己看。
题解:不知道这道题和LCA&RMQ有基本关系。
网上题解都是dp+线段树。
不过我觉得不用线段树,单调队列就可以(怎么单调很好想)。
然而WA了,不理解,拿标称对拍,拍了一大堆数据都没问题,不知道为何WA
欢迎大神帮我查查错。
//6.4 再想了一下单调队列在某些情况下的确有问题,所以还是要线段树,不过为什么对拍都能过,我不知道。。

1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 #include<cstring> 5 #define ll long long 6 using namespace std; 7 const int maxn=5e4+5; 8 9 int a[maxn],b[maxn],anxt[maxn],bnxt[maxn]; 10 int aque[maxn],bque[maxn],at,bt; 11 ll f[maxn][2]; 12 int n,s; 13 14 int main(){ 15 scanf("%d%d",&n,&s); 16 for(int i=1;i<=n;i++) 17 scanf("%d%d",&a[i],&b[i]); 18 19 for(int i=1;i<=n;i++){ 20 while(at&&a[aque[at]]>=a[i]) at--; 21 anxt[i]=aque[at]; 22 aque[++at]=i; 23 24 while(bt&&b[bque[bt]]<=b[i]) bt--; 25 bnxt[i]=bque[bt]; 26 bque[++bt]=i; 27 } 28 29 memset(f,127,sizeof(f)); 30 f[n][0]=abs(a[n]-s),f[n][1]=abs(b[n]-s); 31 32 for(int i=n;i>=1;i--){ 33 f[anxt[i]][0]=min(f[anxt[i]][0],f[i][0]+abs(a[i]-a[anxt[i]])); 34 f[anxt[i]][1]=min(f[anxt[i]][1],f[i][0]+abs(a[i]-b[anxt[i]])); 35 f[bnxt[i]][0]=min(f[bnxt[i]][0],f[i][1]+abs(b[i]-a[bnxt[i]])); 36 f[bnxt[i]][1]=min(f[bnxt[i]][1],f[i][1]+abs(b[i]-b[bnxt[i]])); 37 } 38 39 printf("%lld ",min(f[0][0],f[0][1])); 40 return 0; 41 }
那么我们来总结一下。
感觉LCA常做的有,处理树上路径长度做减法,判断路径是否包含;特殊一点,就是要你统计路径信息,这个就要在求的过程中合并时下功夫;常用的技巧是,DFS+树状数组辅助统计,打标记+树形dp统计。
RMQ貌似还没看到什么比较特殊的。。有一道论文题还可以。。二维RMQ?找个时间填掉。。