专题训练之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 }
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 }
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 }
练习题:
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
注:以上两题通过与标程对拍也没找到错误的点,希望有人可以提供一下有用的数据,谢谢!