【Homework】LCA&RMQ

我校是神校,作业竟然选自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\n",ask(l,r));
36     }
37     return 0;
38 }
View Code

 

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\n",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\n",ans);
66         }
67     }
68 }
View Code

 

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\n",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 }
View Code

 

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\n",ans[i]);
91         
92     return 0;
93 }
View Code

 

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\n",ans);
77     return 0;
78 }
View Code

 

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\n",min(f[0][0],f[0][1]));
40     return 0;
41 }
WA Code

 

那么我们来总结一下。

感觉LCA常做的有,处理树上路径长度做减法,判断路径是否包含;特殊一点,就是要你统计路径信息,这个就要在求的过程中合并时下功夫;常用的技巧是,DFS+树状数组辅助统计,打标记+树形dp统计。

RMQ貌似还没看到什么比较特殊的。。有一道论文题还可以。。二维RMQ?找个时间填掉。。

posted @ 2015-06-02 00:30  CyanNode  阅读(279)  评论(0编辑  收藏  举报