专题训练之LCA

推荐几个博客:https://www.cnblogs.com/JVxie/p/4854719.html Tarjan离线算法的基本思路及其算法实现

https://blog.csdn.net/shahdza/article/details/7779356 LCA题集

http://www.cnblogs.com/zhouzhendong/p/7256007.html LCA的三种算法介绍

 

模板(题):

1.(POJ1470)http://poj.org/problem?id=1470

Tarjan离线算法

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<iostream>
  5 using namespace std;
  6 const int maxn=1010;
  7 const int maxm=500010;
  8 struct Edge{
  9     int to,nxt;
 10 }edge[maxn*2];
 11 struct Query{
 12     int q,nxt;
 13     int index;
 14 }query[maxm*2];
 15 int f[maxn],anc[maxn];
 16 bool vis[maxn];
 17 int head[maxn],tot;
 18 int ans[maxm],h[maxm],tt,Q;
 19 bool flag[maxn];
 20 int num[maxn];
 21 
 22 int find(int x)
 23 {
 24     if ( f[x]==-1 ) return x;
 25     return f[x]=find(f[x]);
 26 }
 27 
 28 void merge(int x,int y)
 29 {
 30     int fx=find(x);
 31     int fy=find(y);
 32     if ( fx!=fy ) f[fx]=fy;
 33 }
 34 
 35 void addedge(int u,int v)
 36 {
 37     edge[tot].to=v;
 38     edge[tot].nxt=head[u];
 39     head[u]=tot++;
 40 }
 41 
 42 void addquery(int u,int v,int index)
 43 {
 44     query[tt].q=v;
 45     query[tt].nxt=h[u];
 46     query[tt].index=index;
 47     h[u]=tt++;
 48     query[tt].q=u;
 49     query[tt].nxt=h[v];
 50     query[tt].index=index;
 51     h[v]=tt++;
 52 }
 53 
 54 void init()
 55 {
 56     tot=0;
 57     memset(head,-1,sizeof(head));
 58     tt=0;
 59     memset(h,-1,sizeof(h));
 60     memset(vis,false,sizeof(vis));
 61     memset(f,-1,sizeof(f));
 62     memset(anc,0,sizeof(anc));
 63 }
 64 
 65 void LCA(int u)
 66 {
 67     anc[u]=u;
 68     vis[u]=true;
 69     for ( int i=head[u];i!=-1;i=edge[i].nxt )
 70     {
 71         int v=edge[i].to;
 72         if ( vis[v] ) continue;
 73         LCA(v);
 74         merge(u,v);
 75         anc[find(u)]=u;
 76     }
 77     for ( int i=h[u];i!=-1;i=query[i].nxt )
 78     {
 79         int v=query[i].q;
 80         if ( vis[v] ) ans[query[i].index]=anc[find(v)];
 81     }
 82 }
 83 
 84 int main()
 85 {
 86     int n,u,v,k;
 87     while ( scanf("%d",&n)!=EOF )
 88     {
 89         init();
 90         memset(flag,false,sizeof(flag));
 91         for ( int i=1;i<=n;i++ )
 92         {
 93             scanf("%d:(%d)",&u,&k);
 94             while ( k-- )
 95             {
 96                 scanf("%d",&v);
 97                 flag[v]=true;
 98                 addedge(u,v);
 99                 addedge(v,u);
100             }
101         }
102         scanf("%d",&Q);
103         for ( int i=0;i<Q;i++ )
104         {
105             char ch;
106             cin>>ch;
107             scanf("%d %d)",&u,&v);
108             addquery(u,v,i);
109         }
110         int root;
111         for ( int i=1;i<=n;i++ )
112         {
113             if ( !flag[i] )
114             {
115                 root=i;
116                 break;
117             }
118         }
119         LCA(root);
120         memset(num,0,sizeof(num));
121         for ( int i=0;i<Q;i++ ) num[ans[i]]++;
122         for ( int i=1;i<=n;i++ )
123         {
124             if ( num[i]>0 ) printf("%d:%d\n",i,num[i]);
125         }
126     }
127     return 0;
128 }
POJ1470

 

2.(POJ1330)http://poj.org/problem?id=1330

倍增法在线算法

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<queue>
  5 using namespace std;
  6 const int maxn=1e4+10;
  7 const int DEG=20;
  8 struct Edge{
  9     int to,nxt;
 10 }edge[maxn*2];
 11 int head[maxn],tot;
 12 int fa[maxn][DEG]; //fa[i][j]表示节点i的第2^j个祖先 
 13 int deg[maxn];
 14 bool flag[maxn];
 15 
 16 void addedge(int u,int v)
 17 {
 18     edge[tot].to=v;
 19     edge[tot].nxt=head[u];
 20     head[u]=tot++;
 21 }
 22 
 23 void init()
 24 {
 25     tot=0;
 26     memset(head,-1,sizeof(head));
 27 }
 28 
 29 void BFS(int root)
 30 {
 31     queue<int>que;
 32     deg[root]=0;
 33     fa[root][0]=root;
 34     que.push(root);
 35     while ( !que.empty() ) 
 36     {
 37         int tmp=que.front();
 38         que.pop();
 39         for ( int i=1;i<DEG;i++ ) fa[tmp][i]=fa[fa[tmp][i-1]][i-1];
 40         for ( int i=head[tmp];i!=-1;i=edge[i].nxt )
 41         {
 42             int v=edge[i].to;
 43             if ( v==fa[tmp][0] ) continue;
 44             deg[v]=deg[tmp]+1;
 45             fa[v][0]=tmp;
 46             que.push(v);
 47         }
 48     }
 49 }
 50 
 51 int LCA(int u,int v)
 52 {
 53     if ( deg[u]>deg[v] ) swap(u,v);
 54     int hu=deg[u],hv=deg[v];
 55     int tu=u,tv=v;
 56     for ( int det=hv-hu,i=0;det;det>>=1,i++ )
 57     {
 58         if ( det&1 ) tv=fa[tv][i];
 59     }
 60     if ( tu==tv ) return tu;
 61     for ( int i=DEG-1;i>=0;i-- )
 62     {
 63         if ( fa[tu][i]==fa[tv][i] ) continue;
 64         tu=fa[tu][i];
 65         tv=fa[tv][i];
 66     }
 67     return fa[tu][0];
 68 }
 69 
 70 int main()
 71 {
 72     int T,n,u,v;
 73     scanf("%d",&T);
 74     while ( T-- )
 75     {
 76         scanf("%d",&n);
 77         init();
 78         memset(flag,false,sizeof(flag));
 79         for ( int i=1;i<n;i++ )
 80         {
 81             scanf("%d%d",&u,&v);
 82             addedge(u,v);
 83             addedge(v,u);
 84             flag[v]=true;
 85         }
 86         int root;
 87         for ( int i=1;i<=n;i++ )
 88         {
 89             if ( !flag[i] )
 90             {
 91                 root=i;
 92                 break;
 93             }
 94         }
 95         BFS(root);
 96         scanf("%d%d",&u,&v);
 97         printf("%d\n",LCA(u,v));
 98     }
 99     return 0;
100 }
POJ1330

ST+DFS在线算法

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 using namespace std;
  5 const int maxn=10010;
  6 int rmq[maxn*2]; //即欧拉序列对应的深度序列 
  7 struct ST
  8 {
  9     int mm[2*maxn];
 10     int dp[2*maxn][20]; //最小值对应的下标
 11     void init(int n)
 12     {
 13         mm[0]=-1;
 14         for ( int i=1;i<=n;i++ )
 15         {
 16             mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
 17             dp[i][0]=i;
 18         }
 19         for ( int j=1;j<=mm[n];j++ )
 20         {
 21             for ( int i=1;i+(1<<j)-1<=n;i++ ) 
 22             {
 23                 if ( rmq[dp[i][j-1]]<rmq[dp[i+(1<<(j-1))][j-1]] ) dp[i][j]=dp[i][j-1];
 24                 else dp[i][j]=dp[i+(1<<(j-1))][j-1];    
 25             }
 26         }
 27     } 
 28     int query(int a,int b) //查询[a,b] 之间最小值的下标 
 29     {
 30         if ( a>b ) swap(a,b);
 31         int k=mm[b-a+1];
 32         if ( rmq[dp[a][k]]<=rmq[dp[b-(1<<k)+1][k]] ) return dp[a][k];
 33         else return dp[b-(1<<k)][k];
 34     }
 35 };
 36 struct Edge{
 37     int to,nxt;
 38 }edge[maxn*2];
 39 int tot,head[maxn];
 40 int F[maxn],P[maxn],cnt; //F为欧拉序(即DFS遍历的顺序),长度为2*n-1,从1开始;P为所有点第一次在F中出现的位置 
 41 ST st;
 42 
 43 void init()
 44 {
 45     tot=0;
 46     memset(head,-1,sizeof(head));
 47 }
 48 
 49 void addedge(int u,int v) //加边,无向边需要加两次 
 50 {
 51     edge[tot].to=v;
 52     edge[tot].nxt=head[u];
 53     head[u]=tot++;
 54 }
 55 
 56 void dfs(int u,int pre,int dep)
 57 {
 58     F[++cnt]=u;
 59     rmq[cnt]=dep;
 60     P[u]=cnt;
 61     for ( int i=head[u];i!=-1;i=edge[i].nxt )
 62     {
 63         int v=edge[i].to;
 64         if ( v==pre ) continue;
 65         dfs(v,u,dep+1);
 66         F[++cnt]=u;
 67         rmq[cnt]=dep;
 68     }
 69 }
 70 
 71 void LCA_init(int root,int num) //查询LCA前的初始化 
 72 {
 73     cnt=0;
 74     dfs(root,root,0);
 75     st.init(2*num-1);
 76 }
 77 
 78 int query(int u,int v) //查询LCA(u,v)的编号 
 79 {
 80     return F[st.query(P[u],P[v])];
 81 }
 82 bool flag[maxn];
 83 
 84 int main()
 85 {
 86     int T,N,u,v;
 87     scanf("%d",&T);
 88     while ( T-- )
 89     {
 90         scanf("%d",&N);
 91         init();
 92         memset(flag,false,sizeof(flag));
 93         for ( int i=1;i<N;i++ )
 94         {
 95             scanf("%d%d",&u,&v);
 96             addedge(u,v);
 97             addedge(v,u);
 98             flag[v]=true;
 99         }
100         int root;
101         for ( int i=1;i<=N;i++ )
102         {
103             if ( !flag[i] ) 
104             {
105                 root=i;
106                 break;
107             }
108         }
109         LCA_init(root,N);
110         scanf("%d%d",&u,&v);
111         printf("%d\n",query(u,v));
112     }
113     return 0;
114 }
POJ1330

 

 

练习题:

1.(HDOJ2586)http://acm.hdu.edu.cn/showproblem.php?pid=2586

题意:求给定两点之间的距离

分析:如果t是u,v的最近公共祖先,那么d[u,v]=d[u,root]+d[v,root]-2*d[t,root],所以我们只需要在倍增算法的BFS中每次更新深度时同时把距离一起更新掉即可

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<queue>
  5 using namespace std;
  6 const int maxn=4e4+10;
  7 const int DEG=20;
  8 const int inf=1e9;
  9 struct Edge{
 10     int to,nxt,w;
 11 }edge[maxn*2];
 12 int head[maxn],tot;
 13 int fa[maxn][DEG]; //fa[i][j]表示节点i的第2^j个祖先 
 14 int deg[maxn];
 15 bool flag[maxn];
 16 int n,d[maxn];
 17 
 18 void addedge(int u,int v,int w)
 19 {
 20     edge[tot].to=v;
 21     edge[tot].nxt=head[u];
 22     edge[tot].w=w;
 23     head[u]=tot++;
 24 }
 25 
 26 void init()
 27 {
 28     tot=0;
 29     memset(head,-1,sizeof(head));
 30 }
 31 
 32 void BFS(int root)
 33 {
 34     queue<int>que;
 35     for ( int i=1;i<=n;i++ ) d[i]=inf;
 36     d[root]=0;
 37     deg[root]=0;
 38     fa[root][0]=root;
 39     que.push(root);
 40     while ( !que.empty() ) 
 41     {
 42         int tmp=que.front();
 43         que.pop();
 44         for ( int i=1;i<DEG;i++ ) fa[tmp][i]=fa[fa[tmp][i-1]][i-1];
 45         for ( int i=head[tmp];i!=-1;i=edge[i].nxt )
 46         {
 47             int v=edge[i].to;
 48             int w=edge[i].w;
 49             if ( v==fa[tmp][0] ) continue;
 50             deg[v]=deg[tmp]+1;
 51             d[v]=min(d[v],d[tmp]+w);
 52             fa[v][0]=tmp;
 53             que.push(v);
 54         }
 55     }
 56 }
 57 
 58 int LCA(int u,int v)
 59 {
 60     if ( deg[u]>deg[v] ) swap(u,v);
 61     int hu=deg[u],hv=deg[v];
 62     int tu=u,tv=v;
 63     for ( int det=hv-hu,i=0;det;det>>=1,i++ )
 64     {
 65         if ( det&1 ) tv=fa[tv][i];
 66     }
 67     if ( tu==tv ) return tu;
 68     for ( int i=DEG-1;i>=0;i-- )
 69     {
 70         if ( fa[tu][i]==fa[tv][i] ) continue;
 71         tu=fa[tu][i];
 72         tv=fa[tv][i];
 73     }
 74     return fa[tu][0];
 75 }
 76 
 77 int main()
 78 {
 79     int T,u,v,w,m,x,ans;
 80     scanf("%d",&T);
 81     while ( T-- )
 82     {
 83         scanf("%d%d",&n,&m);
 84         init();
 85         memset(flag,false,sizeof(flag));
 86         for ( int i=1;i<n;i++ )
 87         {
 88             scanf("%d%d%d",&u,&v,&w);
 89             addedge(u,v,w);
 90             addedge(v,u,w);
 91             flag[v]=true;
 92         }
 93         int root;
 94         for ( int i=1;i<=n;i++ )
 95         {
 96             if ( !flag[i] )
 97             {
 98                 root=i;
 99                 break;
100             }
101         }
102         BFS(root);
103         for ( int i=1;i<=m;i++ )
104         {
105             scanf("%d%d",&u,&v);
106             x=LCA(u,v);
107             ans=d[u]+d[v]-2*d[x];
108             printf("%d\n",ans);
109         }
110     }
111     return 0;
112 }
HDOJ2586

 

2.(HDOJ2874)http://acm.hdu.edu.cn/showproblem.php?pid=2874

题意:给出一个森林,求给定两点之间的距离

分析:Tarjan离线算法,有多个根节点,每次询问时判断两个点是否处于同一个树上。LCA传入参数时需要传入当前的点,距离根节点的距离,根节点的编号。但是此题卡内存

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<iostream>
  5 using namespace std;
  6 const int maxn=1e4+10;
  7 const int maxm=1e6+10;
  8 struct Edge{
  9     int to,nxt,w;
 10 }edge[maxn*2];
 11 struct Query{
 12     int q,nxt;
 13     int index;
 14 }query[maxm*2];
 15 int f[maxn],anc[maxn];
 16 int head[maxn],tot;
 17 int ans[maxm],h[maxm],tt,Q,belong[maxn];
 18 int n,d[maxn];
 19 
 20 int find(int x)
 21 {
 22     if ( f[x]==-1 ) return x;
 23     return f[x]=find(f[x]);
 24 }
 25 
 26 void merge(int x,int y)
 27 {
 28     int fx=find(x);
 29     int fy=find(y);
 30     if ( fx!=fy ) f[fx]=fy;
 31 }
 32 
 33 void addedge(int u,int v,int w)
 34 {
 35     edge[tot].to=v;
 36     edge[tot].nxt=head[u];
 37     edge[tot].w=w;
 38     head[u]=tot++;
 39 }
 40 
 41 void addquery(int u,int v,int index)
 42 {
 43     query[tt].q=v;
 44     query[tt].nxt=h[u];
 45     query[tt].index=index;
 46     h[u]=tt++;
 47     query[tt].q=u;
 48     query[tt].nxt=h[v];
 49     query[tt].index=index;
 50     h[v]=tt++;
 51 }
 52 
 53 void init()
 54 {
 55     tot=0;
 56     memset(head,-1,sizeof(head));
 57     tt=0;
 58     memset(h,-1,sizeof(h));
 59     memset(f,-1,sizeof(f));
 60     memset(anc,0,sizeof(anc));
 61 }
 62 
 63 void LCA(int u,int deep,int root)
 64 {
 65     anc[u]=u;
 66     belong[u]=root;
 67     d[u]=deep;
 68     for ( int i=head[u];i!=-1;i=edge[i].nxt )
 69     {
 70         int v=edge[i].to;
 71         int w=edge[i].w;
 72         if ( belong[v]!=-1 ) continue;
 73         LCA(v,deep+w,root);
 74         merge(u,v);
 75         anc[find(u)]=u;
 76     }
 77     for ( int i=h[u];i!=-1;i=query[i].nxt )
 78     {
 79         int v=query[i].q;
 80         if ( belong[v]==root ) 
 81         {
 82             int sum=d[u]+d[v]-2*d[anc[find(v)]];
 83             ans[query[i].index]=sum;
 84         }
 85     }
 86 }
 87 
 88 int main()
 89 {
 90     int u,v,w,k,m,q,x;
 91     while ( scanf("%d%d%d",&n,&m,&Q)!=EOF )
 92     {
 93         init();
 94         for ( int i=1;i<=m;i++ )
 95         {
 96             scanf("%d%d%d",&u,&v,&w);
 97             addedge(u,v,w);
 98             addedge(v,u,w);
 99         }
100         for ( int i=0;i<Q;i++ )
101         {
102             ans[i]=-1;
103             scanf("%d%d",&u,&v);
104             addquery(u,v,i);
105         }
106         memset(belong,-1,sizeof(belong));
107         memset(d,-1,sizeof(d));
108         for ( int i=1;i<=n;i++ )
109         {
110             if ( belong[i]==-1 ) LCA(i,0,i);
111         }
112         for ( int i=0;i<Q;i++ ) 
113         {
114             if ( ans[i]!=-1 ) printf("%d\n",ans[i]);
115             else printf("Not connected\n");    
116         }
117     }
118     return 0;
119 }
HDOJ2874(MLE)
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<queue>
  5 #include<vector>
  6 using namespace std;
  7 typedef long long LL;
  8 
  9 const int maxm=2e4+10;
 10 const int maxn=1e4+10;
 11 const int maxq=2e6+10;
 12 struct Node{
 13     int to;
 14     int w;
 15     int next;
 16 }e[maxm];
 17 int eh[maxn],dis[maxn],pre[maxn],etol,vis[maxn];
 18 struct Query{
 19     int to;
 20     int index;
 21     int next;
 22 }qe[maxq];
 23 int qh[maxn],ans[maxq/2],qtol;
 24 int n,m,c;
 25 
 26 void init()
 27 {
 28     etol=qtol=0;
 29     memset(eh,-1,sizeof(eh));
 30     memset(qh,-1,sizeof(qh));
 31 }
 32 
 33 void add1(int u,int v,int w)
 34 {
 35     e[etol].to=v;
 36     e[etol].w=w;
 37     e[etol].next=eh[u];
 38     eh[u]=etol++;
 39 }
 40 
 41 void add2(int u,int v,int id)
 42 {
 43     qe[qtol].index=id;
 44     qe[qtol].to=v;
 45     qe[qtol].next=qh[u];
 46     qh[u]=qtol++;
 47 }
 48 
 49 int Find(int u)
 50 {
 51     if(pre[u]!=u) pre[u]=Find(pre[u]);
 52     return pre[u];
 53 }
 54 
 55 void LCA(int u,int deep,int root)
 56 {
 57     pre[u]=u;
 58     dis[u]=deep;
 59     vis[u]=root;
 60     for(int i=eh[u];~i;i=e[i].next)
 61     {
 62         int v=e[i].to;
 63         if(vis[v]==-1)
 64         {
 65             LCA(v,deep+e[i].w,root);
 66             pre[v]=u;
 67         }
 68     }
 69     for(int i=qh[u];~i;i=qe[i].next)
 70     {
 71         int v=qe[i].to;
 72         if(vis[v]==root)
 73             ans[qe[i].index]=dis[v]+dis[u]-2*dis[Find(v)];
 74     }
 75 }
 76 
 77 
 78 int main()
 79 {
 80     while(~scanf("%d%d%d",&n,&m,&c))
 81     {
 82         int u,v,w;
 83         init();
 84         while(m--)
 85         {
 86             scanf("%d%d%d",&u,&v,&w);
 87             add1(u,v,w);
 88             add1(v,u,w);    
 89         }
 90         for(int i=0;i<c;i++)
 91         {
 92             scanf("%d%d",&u,&v);
 93             ans[i]=-1;
 94             add2(u,v,i);
 95             add2(v,u,i);
 96         }
 97         memset(vis,-1,sizeof(vis));
 98         for(int i=1;i<=n;i++){
 99             if(vis[i]==-1)
100                 LCA(i,0,i);
101         }
102         for(int i=0;i<c;i++)
103         {
104             if(ans[i]==-1) puts("Not connected");
105             else printf("%d\n",ans[i]);
106         }
107     }
108     return 0;
109 } 
HDOJ2874(参考的AC代码)

 

3.(HDOJ5044)http://acm.hdu.edu.cn/showproblem.php?pid=5044

题意:给出一颗树,有m个操作,若为add1 u v w表示从u到v经过点的点权都+w,add2 u v w表示从u到v经过点的边权都+w

分析:以下解释摘自:https://blog.csdn.net/hongrock/article/details/39616757

找到U, V的最近公共祖先X。

add[i][0]表示第i个点的权值。

des[i]表示第i个点在计算完之后,应该减少的权值。

add[i][1]表示第i个点跟其父结点之间的边的权值。

对于操作1有:add[u][0]+=w add[v][0]+=w add[X][0]-=w des[X]+=w;

对于操作2有:add[u][1]+=w add[v][1]+=w add[X][1]-=2*w

关于操作的解释:

对于第一种操作,肯定U到X的路径的结点增加K,V到X的路径也增加K。

所以我们可以通过将add[U][0]和add[V][0]的信息不断传递上去,直到X为止。

由于U和V都会传递一个K给X,所以add[X][0]减掉一个K。

处理完X了,K不能再传给其父节点,所以再用des[X]减掉一次。

对于第二种操作,同理,也是不断将信息向上传递。但由于这里是边,不会算两次,所以直接在X的位置减掉两倍K即可。

 

完成以上后,进行bfs,每次将叶子节点(入度为0的点)不断投入栈中,求出每个点/边的权值

最后注意下N=1的时候,虽然没有边,但是输出边权还是要留个空行给它。

注意:此题卡空间卡时间,要输入输出外挂,还要人工手动加栈,初始化要少等等优化

  1 #pragma comment(linker, "/STACK:1024000000,1024000000")  
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<queue>
  6 using namespace std;
  7 typedef long long ll;
  8 const int maxn=1e5+10;
  9 const int DEG=20;
 10 struct Edge{
 11     int to,nxt;
 12 }edge[maxn*2];
 13 int head[maxn],tot;
 14 int fa[maxn][DEG]; //fa[i][j]表示节点i的第2^j个祖先 
 15 int deg[maxn];
 16 ll add[maxn][2],node[maxn],edg[maxn],des[maxn];
 17 int du[maxn],tp[maxn];
 18 bool flag[maxn];
 19 
 20 void addedge(int u,int v)
 21 {
 22     edge[tot].to=v;
 23     edge[tot].nxt=head[u];
 24     head[u]=tot++;
 25 }
 26 
 27 void init()
 28 {
 29     tot=0;
 30     memset(head,-1,sizeof(head));
 31 }
 32 
 33 void BFS(int root)
 34 {
 35     queue<int>que;
 36     deg[root]=0;
 37     fa[root][0]=root;
 38     que.push(root);
 39     while ( !que.empty() ) 
 40     {
 41         int tmp=que.front();
 42         que.pop();
 43         for ( int i=1;i<DEG;i++ ) fa[tmp][i]=fa[fa[tmp][i-1]][i-1];
 44         for ( int i=head[tmp];i!=-1;i=edge[i].nxt )
 45         {
 46             int v=edge[i].to;
 47             if ( v==fa[tmp][0] ) continue;
 48             tp[v]=i/2+1;
 49             du[tmp]++;
 50             deg[v]=deg[tmp]+1;
 51             fa[v][0]=tmp;
 52             que.push(v);
 53         }
 54     }
 55 }
 56 
 57 int LCA(int u,int v)
 58 {
 59     if ( deg[u]>deg[v] ) swap(u,v);
 60     int hu=deg[u],hv=deg[v];
 61     int tu=u,tv=v;
 62     for ( int det=hv-hu,i=0;det;det>>=1,i++ )
 63     {
 64         if ( det&1 ) tv=fa[tv][i];
 65     }
 66     if ( tu==tv ) return tu;
 67     for ( int i=DEG-1;i>=0;i-- )
 68     {
 69         if ( fa[tu][i]==fa[tv][i] ) continue;
 70         tu=fa[tu][i];
 71         tv=fa[tv][i];
 72     }
 73     return fa[tu][0];
 74 }
 75 
 76 void bfs(int n)
 77 {
 78     queue<int>que;
 79     for ( int i=1;i<=n;i++ )
 80     {
 81         if ( !du[i] ) que.push(i);
 82     }
 83     while ( !que.empty() )
 84     {
 85         int u=que.front();
 86         que.pop();
 87         node[u]=add[u][0];
 88         add[u][0]-=des[u];
 89         int v=fa[u][0];
 90         add[v][0]+=add[u][0];
 91         add[v][1]+=add[u][1];
 92         edg[tp[u]]+=add[u][1];
 93         if ( !(--du[v]) ) que.push(v);
 94     }
 95 }
 96 
 97 inline bool scan_d(int &num)  
 98 {
 99     char in;bool IsN=false;
100     in=getchar();
101     if(in==EOF) return false;
102     while(in!='-'&&(in<'0'||in>'9')) in=getchar();
103     if(in=='-'){ IsN=true;num=0;}
104     else num=in-'0';
105     while(in=getchar(),in>='0'&&in<='9'){
106             num*=10,num+=in-'0';
107     }
108     if(IsN) num=-num;
109     return true;
110 }
111 
112 inline void out(long long x) {  
113     if(x>9) out(x/10);  
114     putchar(x%10+'0');  
115 }  
116 
117 int main()
118 {
119     int T,n,u,v,w,q,h,l;
120     char op[10];
121     scan_d(T);
122     for ( h=1;h<=T;h++ )
123     {
124         scan_d(n);
125         scan_d(q);
126         init();
127         for ( int i=1;i<=n;i++ )
128         {
129             //flag[i]=false;
130             add[i][0]=add[i][1]=0;
131             des[i]=du[i]=node[i]=edg[i]=0;
132         }
133         //memset(flag,false,sizeof(flag));
134         //memset(add,0,sizeof(add));
135         //memset(des,0,sizeof(des));
136         //memset(du,0,sizeof(du));
137         //memset(node,0,sizeof(node));
138         //memset(edg,0,sizeof(edg));
139         for ( int i=1;i<n;i++ )
140         {
141             scan_d(u);
142             scan_d(v);
143             addedge(u,v);
144             addedge(v,u);
145         }
146         int root=1;
147         /*
148         for ( int i=1;i<=n;i++ )
149         {
150             if ( !flag[i] )
151             {
152                 root=i;
153                 break;
154             }
155         }
156         */
157         BFS(root);
158         while ( q-- )
159         {
160             scanf("%s",op);
161             scan_d(u);
162             scan_d(v);
163             scan_d(w);
164             l=LCA(u,v);
165             if ( op[3]=='1' )
166             {
167                 add[u][0]+=w;
168                 add[v][0]+=w;
169                 add[l][0]-=w;
170                 des[l]+=w;
171             }
172             else
173             {
174                 add[u][1]+=w;
175                 add[v][1]+=w;
176                 add[l][1]-=2*w;
177             }
178         }
179         bfs(n);
180         printf("Case #%d:\n",h);
181         for ( int i=1;i<=n;i++ ) 
182         {
183             out(node[i]);
184             if ( i!=n ) printf(" ");
185             else printf("\n");
186         }
187         for ( int i=1;i<n;i++ )
188         {
189             out(edg[i]);
190             if ( i!=(n-1) ) printf(" ");
191             else printf("\n");
192         }
193         if ( n==1 ) printf("\n");
194     }
195     return 0;
196 }
HDOJ5044(TLE)

 

4.(HDOJ4547)http://acm.hdu.edu.cn/showproblem.php?pid=4547

分析:从节点a出发,a到任意一个子节点只需要1步,a到任意一个父节点为两个点的距离差。所以询问给出u,v时,先求出l=lca(u,v),ans=dis[u]-dis[l],如果v于l不是同一个点则ans++(从l一步到达v)

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<queue>
  5 #include<map>
  6 #include<string>
  7 #include<iostream>
  8 using namespace std;
  9 const int maxn=1e5+10;
 10 const int DEG=20;
 11 struct Edge{
 12     int to,nxt,w;
 13 }edge[maxn*2];
 14 int head[maxn],tot;
 15 int fa[maxn][DEG]; //fa[i][j]表示节点i的第2^j个祖先 
 16 int deg[maxn];
 17 bool flag[maxn];
 18 int dis[maxn];
 19 map<string,int>mp;
 20 
 21 void addedge(int u,int v,int w)
 22 {
 23     edge[tot].to=v;
 24     edge[tot].nxt=head[u];
 25     edge[tot].w=w;
 26     head[u]=tot++;
 27 }
 28 
 29 void init()
 30 {
 31     tot=0;
 32     memset(head,-1,sizeof(head));
 33 }
 34 
 35 void BFS(int root)
 36 {
 37     queue<int>que;
 38     deg[root]=0;
 39     fa[root][0]=root;
 40     que.push(root);
 41     while ( !que.empty() ) 
 42     {
 43         int tmp=que.front();
 44         que.pop();
 45         for ( int i=1;i<DEG;i++ ) fa[tmp][i]=fa[fa[tmp][i-1]][i-1];
 46         for ( int i=head[tmp];i!=-1;i=edge[i].nxt )
 47         {
 48             int v=edge[i].to;
 49             int w=edge[i].w;
 50             if ( v==fa[tmp][0] ) continue;
 51             dis[v]=dis[tmp]+w;
 52             deg[v]=deg[tmp]+1;
 53             fa[v][0]=tmp;
 54             que.push(v);
 55         }
 56     }
 57 }
 58 
 59 int LCA(int u,int v)
 60 {
 61     if ( deg[u]>deg[v] ) swap(u,v);
 62     int hu=deg[u],hv=deg[v];
 63     int tu=u,tv=v;
 64     for ( int det=hv-hu,i=0;det;det>>=1,i++ )
 65     {
 66         if ( det&1 ) tv=fa[tv][i];
 67     }
 68     if ( tu==tv ) return tu;
 69     for ( int i=DEG-1;i>=0;i-- )
 70     {
 71         if ( fa[tu][i]==fa[tv][i] ) continue;
 72         tu=fa[tu][i];
 73         tv=fa[tv][i];
 74     }
 75     return fa[tu][0];
 76 }
 77 
 78 int main()
 79 {
 80     int T,n,q,u,v,w,num,l,ans;
 81     string s1,s2;
 82     scanf("%d",&T);
 83     while ( T-- )
 84     {
 85         scanf("%d%d",&n,&q);
 86         mp.clear();
 87         init();
 88         num=0;
 89         memset(flag,false,sizeof(flag));
 90         memset(dis,0,sizeof(dis));
 91         for ( int i=1;i<n;i++ )
 92         {
 93             cin>>s1>>s2;
 94             if ( !mp[s1] ) mp[s1]=++num;
 95             if ( !mp[s2] ) mp[s2]=++num;
 96             v=mp[s1];
 97             u=mp[s2];
 98             addedge(u,v,1);
 99             flag[v]=true;
100         }
101         int root;
102         for ( int i=1;i<=n;i++ )
103         {
104             if ( !flag[i] )
105             {
106                 root=i;
107                 break;
108             }
109         }
110         BFS(root);
111         while ( q-- )
112         {
113             cin>>s1>>s2;
114             u=mp[s1];
115             v=mp[s2];
116             l=LCA(u,v);
117             ans=dis[u]-dis[l];
118             if ( l!=v ) ans++;
119             printf("%d\n",ans);
120         }
121     }
122     return 0;
123 }
HDOJ4547

 

5.(HDOJ5274)http://acm.hdu.edu.cn/showproblem.php?pid=5274

题意:有一颗树,有n个点,每个点都有个点权。现在有q个询问,总共有两种操作
0 x y代表把编号为x点的权值变成y

1 x y求出点x到点y的路径上出现点权值为奇数的点,若都为偶数则输出-1。题目保证最多只有一个为奇数的点权值

分析:官方题解:

题目里有一个很神奇的性质:路径上最多只有一个数出现奇数次。

这应该马上想到异或。因为异或两次和没异或是等价的。此外异或满足区间减性质。

因为有修改,我们很自然地想到用数据结构维护。

最无脑的就是直接上树链剖分或是Splay维护区间xor值即可。

仔细想一想,发现可以利用LCA消去“树上路径”,转化为根到x路径上求xor值。

我们可以很经典地直接使用线段树或树状数组维护dfs序。 

有一个很强的trick就是权值可以为0!

所以比如路径上有3个0,虽然他们xor值还是0,但是他们是出现了奇数次。

我特意把A[i]说成∈自然数集而不是[0,100000][0,100000][0,100000],就是想尽量不被发现。

怎么避免呢?单独维护0的情况?

有一个很简单的解决方案:直接把读入时所有权值+1,输出的时候再-1即可!

时间复杂度为O(N∗log(N)2)或者O(N∗log(N))

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 using namespace std;
  5 const int maxn=1e5+10;
  6 int rmq[maxn*2]; 
  7 struct ST
  8 {
  9     int mm[2*maxn];
 10     int dp[2*maxn][20];
 11     void init(int n)
 12     {
 13         mm[0]=-1;
 14         for ( int i=1;i<=n;i++ )
 15         {
 16             mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
 17             dp[i][0]=i;
 18         }
 19         for ( int j=1;j<=mm[n];j++ )
 20         {
 21             for ( int i=1;i+(1<<j)-1<=n;i++ ) 
 22             {
 23                 if ( rmq[dp[i][j-1]]<rmq[dp[i+(1<<(j-1))][j-1]] ) dp[i][j]=dp[i][j-1];
 24                 else dp[i][j]=dp[i+(1<<(j-1))][j-1];    
 25             }
 26         }
 27     } 
 28     int query(int a,int b) 
 29     {
 30         if ( a>b ) swap(a,b);
 31         int k=mm[b-a+1];
 32         if ( rmq[dp[a][k]]<=rmq[dp[b-(1<<k)+1][k]] ) return dp[a][k];
 33         else return dp[b-(1<<k)][k];
 34     }
 35 };
 36 struct Edge{
 37     int to,nxt;
 38 }edge[maxn*2];
 39 int tot,head[maxn],in[maxn],out[maxn];
 40 int F[maxn],P[maxn],cnt,now,val[maxn]; 
 41 int bit[maxn*2];
 42 ST st;
 43 
 44 int lowbit(int x)
 45 {
 46     return x&(-x);
 47 }
 48 
 49 void add(int k,int num)
 50 {
 51     while ( k<maxn*2 )
 52     {
 53         bit[k]^=num;
 54         k+=lowbit(k);
 55     }
 56 }
 57 
 58 int sum(int k)
 59 {
 60     int s=0;
 61     while ( k )
 62     {
 63         s^=bit[k];
 64         k-=lowbit(k);
 65     }
 66     return s;
 67 }
 68 
 69 void init()
 70 {
 71     tot=0;
 72     memset(head,-1,sizeof(head));
 73 }
 74 
 75 void addedge(int u,int v) 
 76 {
 77     edge[tot].to=v;
 78     edge[tot].nxt=head[u];
 79     head[u]=tot++;
 80 }
 81 
 82 void dfs(int u,int pre,int dep)
 83 {
 84     F[++cnt]=u;
 85     in[u]=++now;
 86     rmq[cnt]=dep;
 87     P[u]=cnt;
 88     for ( int i=head[u];i!=-1;i=edge[i].nxt )
 89     {
 90         int v=edge[i].to;
 91         if ( v==pre ) continue;
 92         dfs(v,u,dep+1);
 93         F[++cnt]=u;
 94         rmq[cnt]=dep;
 95     }
 96     out[u]=++now;
 97 }
 98 
 99 void LCA_init(int root,int num) 
100 {
101     cnt=0;
102     now=0;
103     dfs(root,root,0);
104     st.init(2*num-1);
105 }
106 
107 int query(int u,int v) 
108 {
109     return F[st.query(P[u],P[v])];
110 }
111 bool flag[maxn];
112 
113 int main()
114 {
115     int T,u,v,q,op,N;
116     scanf("%d",&T);
117     while ( T-- )
118     {
119         scanf("%d%d",&N,&q);
120         init();
121         memset(flag,false,sizeof(flag));
122         memset(bit,0,sizeof(bit));
123         for ( int i=1;i<N;i++ )
124         {
125             scanf("%d%d",&u,&v);
126             addedge(u,v);
127             addedge(v,u);
128             flag[v]=true;
129         }
130         int root;
131         for ( int i=1;i<=N;i++ )
132         {
133             if ( !flag[i] ) 
134             {
135                 root=i;
136                 break;
137             }
138         }
139         LCA_init(root,N);
140         for ( int i=1;i<=N;i++ ) 
141         {
142             scanf("%d",&val[i]);
143             val[i]++;
144             add(in[i],val[i]);
145             add(out[i],val[i]);
146         }
147         //for ( int i=1;i<=N;i++ ) printf("%d %d\n",in[i],out[i]);
148         //for ( int i=1;i<=2*N;i++ ) printf("%d\n",sum(i));
149         while ( q-- )
150         {
151             scanf("%d%d%d",&op,&u,&v);
152             if ( op==0 )
153             {
154                 v++;
155                 add(in[u],val[u]^v);
156                 add(out[u],val[u]^v);
157                 val[u]=v;
158             }
159             else
160             {
161                 int l=query(u,v);
162                 int ans=sum(in[u])^sum(in[v])^val[l];
163                 if ( ans==0 ) printf("-1\n");
164                 else printf("%d\n",ans-1);
165             }    
166         }
167         //for ( int i=1;i<=N;i++ ) printf("%d %d\n",in[i],out[i]);
168         //for ( int i=1;i<=2*N;i++ ) printf("%d\n",sum(i));
169     }
170     return 0;
171 }
HDOJ5274(WA)
 1 #pragma comment(linker, "/STACK:102400000,102400000")
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<vector>
 6 #include<algorithm>
 7 using namespace std;
 8 #define MAXN 100005
 9 #define MAXM 300005
10 #define LOG 19
11 #define lowbit(i) (i & -i)
12 vector<int>vec[MAXN];
13 int _,n,q,cnt;
14 int val[MAXN],uid[MAXN],vid[MAXN],dp[LOG][MAXM],pre[MAXM];
15 void modify(int i,int v)
16 {
17     for(;i < MAXM;i += lowbit(i))
18         pre[i] ^= v;
19 }
20 int sum(int i)
21 {
22     int ans = 0;
23     for(;i;i -= lowbit(i))
24         ans ^= pre[i];
25     return ans;
26 }
27 void dfs(int u,int fa)
28 {
29     for(int i = 0;i < vec[u].size();i++)
30     {
31         int v = vec[u][i];
32         if(v == fa)continue;
33         uid[v] = ++cnt;
34         modify(cnt,val[v]);
35         dp[0][cnt] = v;
36         dfs(v,u);
37         vid[v] = ++cnt;
38         modify(cnt,val[v]);
39         dp[0][cnt] = u;
40     }
41 }
42 void init_rmq()
43 {
44     for(int i = 1;(1 << i) <= cnt;i++)
45         for(int j = 1;j + (1 << i) - 1 <= cnt;j++)
46             dp[i][j] = min(dp[i - 1][j],dp[i - 1][j + (1 << i - 1)]);
47 }
48 int lca(int l,int r)
49 {
50     if(l > r)swap(l,r);
51     int i = 0;
52     for(;l + (1 << i) - 1 <= r;i++);
53     i--;
54     return min(dp[i][l],dp[i][r - (1 << i) + 1]);
55 }
56 int main()
57 {
58     scanf("%d",&_);
59     while(_--)
60     {
61         scanf("%d%d",&n,&q);
62         for(int i = 0;i <= n;i++)vec[i].clear();
63         vec[0].push_back(1) , vec[1].push_back(0);
64         memset(pre,0,sizeof(pre));
65         for(int i = 1;i < n;i++)
66         {
67             int u,v;
68             scanf("%d%d",&u,&v);
69             vec[u].push_back(v),vec[v].push_back(u);
70         }
71         for(int i = 1;i <= n;i++)
72         {
73             scanf("%d",&val[i]);
74             val[i]++;
75         }
76         cnt = 0;
77         dfs(0,-1);
78         init_rmq();
79         for(int i = 0;i < q;i++)
80         {
81             int c,u,v;
82             scanf("%d%d%d",&c,&u,&v);
83             if(c == 0)
84             {
85                 v++;
86                 modify(uid[u],val[u] ^ v);
87                 modify(vid[u],val[u] ^ v);
88                 val[u] = v;
89             }
90             else
91             {
92                 int fa = lca(uid[u],uid[v]);
93                 int ans = sum(uid[u]) ^ sum(uid[v]) ^ val[fa];
94                 if(!ans)puts("-1");
95                 else printf("%d\n",ans - 1);
96             }
97         }
98     }
99 }
HDOJ5274(参考的AC代码)

 

6.(POJ2763)http://poj.org/problem?id=2763

题意:有一颗树,每条边都有自己的边权,起点在s,有q个询问,询问总共有两种操作

A:从当前点移动到x,输出所需要的时间  B:将通过边x的时间改为t

分析:利用RMQ计算LCA所用的,按DFS访问的顺序排列的顶点顺序。这样u到v之间的路径就是在序列中u和v之间的所有边减去往返重复的部分得到的结果。只要令沿叶子方向的边权为正,沿根方向的部分为负,于是有d(u,v)=d(LCA(u,v),u)+d(LCA(u,v),v)可以用bit进行计算

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 using namespace std;
  5 typedef long long ll;
  6 const int maxn=1e5+10;
  7 int rmq[maxn*2];  
  8 struct ST
  9 {
 10     int mm[2*maxn];
 11     int dp[2*maxn][20];
 12     void init(int n)
 13     {
 14         mm[0] = -1;
 15         for(int i = 1;i <= n;i++)
 16         {
 17             mm[i] = ((i&(i-1)) == 0)?mm[i-1]+1:mm[i-1];
 18             dp[i][0] = i;
 19         }
 20         for(int j = 1; j <= mm[n];j++)
 21             for(int i = 1; i + (1<<j) - 1 <= n; i++)
 22                 dp[i][j] = rmq[dp[i][j-1]] < rmq[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
 23     }
 24     int query(int a,int b)
 25     {
 26         if(a > b)swap(a,b);
 27         int k = mm[b-a+1];
 28         return rmq[dp[a][k]] <= rmq[dp[b-(1<<k)+1][k]]?dp[a][k]:dp[b-(1<<k)+1][k];
 29     }
 30 };
 31 
 32 struct Edge{
 33     int to,nxt,w;
 34 }edge[maxn*2];
 35 int tot,head[maxn];
 36 int F[maxn],P[maxn],cnt,n,es[maxn*2]; 
 37 ll bit[maxn*2];
 38 ST st;
 39 
 40 void init()
 41 {
 42     tot=0;
 43     memset(head,-1,sizeof(head));
 44     memset(bit,0,sizeof(bit));
 45 }
 46 
 47 int lowbit(int x)
 48 {
 49     return x&(-x);
 50 }
 51 
 52 void add(int k,int num)
 53 {
 54     while ( k<=n )
 55     {
 56         bit[k]+=num;
 57         k+=lowbit(k);
 58     }
 59 }
 60 
 61 ll sum(int k)
 62 {
 63     ll sum_=0;
 64     while ( k )
 65     {
 66         sum_+=bit[k];
 67         k-=lowbit(k);
 68     }
 69     return sum_;
 70 }
 71 
 72 void addedge(int u,int v,int w) 
 73 {
 74     edge[tot].to=v;
 75     edge[tot].nxt=head[u];
 76     edge[tot].w=w;
 77     head[u]=tot++;
 78 }
 79 
 80 void dfs(int u,int pre,int dep)
 81 {
 82     F[++cnt]=u;
 83     rmq[cnt]=dep;
 84     P[u]=cnt;
 85     for ( int i=head[u];i!=-1;i=edge[i].nxt )
 86     {
 87         int v=edge[i].to;
 88         int w=edge[i].w;
 89         if ( v==pre ) continue;
 90         es[i]=cnt+1;
 91         add(es[i],w);
 92         dfs(v,u,dep+1);
 93         F[++cnt]=u;
 94         es[i^1]=cnt;
 95         add(es[i^1],-w);
 96         rmq[cnt]=dep;
 97     }
 98 }
 99 
100 void LCA_init(int root,int num) 
101 {
102     cnt=0;
103     dfs(root,root,0);
104     st.init(2*num-1);
105 }
106 
107 int query(int u,int v) 
108 {
109     return F[st.query(P[u],P[v])];
110 }
111 bool flag[maxn];
112 
113 int main()
114 {
115     int T,N,q,s,u,v,w,op;
116     while ( scanf("%d%d%d",&N,&q,&s)!=EOF )
117     {
118         init();
119         n=2*N-1;
120         memset(flag,false,sizeof(flag));
121         for ( int i=1;i<N;i++ )
122         {
123             scanf("%d%d%d",&u,&v,&w);
124             addedge(u,v,w);
125             addedge(v,u,w);
126             flag[v]=true;
127         }
128         int root;
129         for ( int i=1;i<=N;i++ )
130         {
131             if ( !flag[i] ) 
132             {
133                 root=i;
134                 break;
135             }
136         }
137         LCA_init(root,N);
138         u=s;
139         //for ( int i=1;i<=n;i++ ) printf("%lld\n",sum(i));
140         //for ( int i=0;i<2*(N-1);i++ ) printf("%d\n",es[i]);
141         while ( q-- )
142         {
143             scanf("%d",&op);
144             if ( op==0 )
145             {
146                 scanf("%d",&v);
147                 int l=query(u,v);
148                 printf("%lld\n",sum(P[u])+sum(P[v])-2*sum(P[l]));
149                 u=v;
150             }
151             else 
152             {
153                 int p,x,k1,k2;
154                 scanf("%d%d",&p,&x);
155                 k1=es[(p-1)*2];
156                 k2=es[((p-1)*2)^1];
157                 add(k1,x-edge[(p-1)*2].w);
158                 add(k2,edge[((p-1)*2)^1].w-x);
159                 edge[(p-1)*2].w=edge[((p-1)*2)^1].w=x;
160             }
161         }
162         //for ( int i=1;i<=n;i++ ) printf("%lld\n",sum(i));
163     }
164     return 0;
165 }
POJ2763(WA)

注:以上两题通过与标程对拍也没找到错误的点,希望有人可以提供一下有用的数据,谢谢!

posted @ 2018-05-28 21:57  HDU_jackyan  阅读(973)  评论(1编辑  收藏  举报