zoukankan      html  css  js  c++  java
  • 【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
    ",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
    ",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 }
    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
    ",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
    ",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
    ",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
    ",min(f[0][0],f[0][1]));
    40     return 0;
    41 }
    WA Code

    那么我们来总结一下。

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

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

  • 相关阅读:
    hdu1430 魔板(康拓展开 bfs预处理)
    网络流EdmondsKarp算法模板理解
    poj3020 建信号塔(匈牙利算法 最小覆盖边集)
    bzoj 2465 小球
    bzoj 1822 冷冻波
    bzoj 1040 骑士
    Codeforces Round #460 (Div. 2)
    bzoj 1072 排列perm
    Codeforces Round #459 (Div. 2)
    bzoj 1087 互不侵犯King
  • 原文地址:https://www.cnblogs.com/xkui/p/4545329.html
Copyright © 2011-2022 走看看