Time Limit: 4000MS | Memory Limit: 65536K | |
Total Submissions: 7918 | Accepted: 2078 |
Description
Input
Output
Sample Input
3 3 1 1 2 1 2 3 2 0 2 1 2 3 0 3
Sample Output
13
这题可以用裸的在线lca做,也可以用树状数组做。
代码一:裸的在线lca,每次更改边的权值,就把该点的子树重新更新一遍。
#include<iostream> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> #include<vector> #include<map> #include<set> #include<queue> #include<stack> #include<string> #include<algorithm> using namespace std; #define ll long long #define inf 0x7fffffff #define maxn 100010 struct node{ int to,len,next,from; }e[2*maxn]; int first[maxn],dep[maxn],f[maxn][30],dist[maxn],pre[maxn]; int lca(int x,int y){ int i; if(dep[x]<dep[y]){ swap(x,y); } for(i=18;i>=0;i--){ if(dep[f[x][i] ]>=dep[y]){ x=f[x][i]; } } if(x==y)return x; for(i=18;i>=0;i--){ if(f[x][i]!=f[y][i]){ x=f[x][i];y=f[y][i]; } } return f[x][0]; } void dfs(int x,int fa){ int i,y; dep[x]=dep[fa]+1; for(i=first[x];i!=-1;i=e[i].next){ y=e[i].to; if(y!=fa){ f[y][0]=x; pre[y]=x; dist[y]=dist[x]+e[i].len; dfs(y,x); } } } int main() { int n,m,i,j,T,q,s,d,c,g,tot,k; while(scanf("%d%d%d",&n,&q,&s)!=EOF) { tot=0; memset(first,-1,sizeof(first)); for(i=1;i<=n-1;i++){ scanf("%d%d%d",&c,&d,&g); e[i].next=first[c];e[i].len=g;e[i].to=d;e[i].from=c; first[c]=i; e[i+n-1].next=first[d];e[i+n-1].len=g;e[i+n-1].to=c;e[i+n-1].from=d; first[d]=i+n-1; } pre[1]=0; dep[0]=0;dist[1]=0; dfs(1,0); for(k=1;k<=18;k++){ for(i=1;i<=n;i++){ f[i][k]=f[f[i][k-1]][k-1]; } } for(i=1;i<=q;i++){ scanf("%d%d",&g,&c); if(g==0){ int t=lca(s,c); printf("%d\n",dist[s]+dist[c]-2*dist[t]); s=c; } else{ scanf("%d",&d); int u=e[c].from; int v=e[c].to; int t1,t2; e[c].len=e[c+n-1].len=d; if(pre[u]==v){ t1=u;t2=v; } else{ t1=v;t2=u; } dist[t1]=dist[t2]+d; dfs(t1,t2); } } } return 0; }代码二:树上两个节点a,b的距离可以转化为dis[a] + dis[b] - 2*dis[lca(a,b)],其中 dis[i] 表示 i 节点到根的距离,由于每次修改一条边,树中在这条边下方的 dis[] 值全都会受到影响,这样每条边都对应这一段这条边的管辖区,可以深搜保存遍历该点的时间戳,st[i] 表示第一次遍历到该点的时间戳,ed[i] 表示回溯到该点时的时间戳,这样每次修改边 i 的时候就可以对区间[st[i],ed[i]]进行成段更新,即位置 ll[i] 上加一个权值,在位置rr[i]+1 上减去这个权值,求和时,sum(ll[i]) 即为该点到根的距离。这里注意初始化的时候,可以把输入的每一条边都当做更新的边,这样初始操作和更新操作都是一样的。
#include<iostream> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> #include<vector> #include<map> #include<set> #include<queue> #include<stack> #include<string> #include<algorithm> using namespace std; #define ll long long #define inf 0x7fffffff #define maxn 100050 struct node{ int len,to,next,from; }e[2*maxn]; int first[maxn],dep[maxn],f[maxn][30],b[maxn],st[maxn],ed[maxn],tot,pre[maxn]; int lowbit(int x){ return x&(-x); } void update(int pos,int num){ while(pos<maxn){ b[pos]+=num;pos+=lowbit(pos); } } int getsum(int pos){ int num=0; while(pos>0){ num+=b[pos];pos-=lowbit(pos); } return num; } void dfs(int x,int fa){ int i,y; dep[x]=dep[fa]+1; st[x]=tot; for(i=first[x];i!=-1;i=e[i].next){ y=e[i].to; if(y!=fa){ f[y][0]=x; pre[y]=x; tot++; dfs(y,x); } } ed[x]=tot; } int lca(int x,int y){ int i; if(dep[x]<dep[y]){ swap(x,y); } for(i=18;i>=0;i--){ if(dep[f[x][i] ]>=dep[y]){ x=f[x][i]; } } if(x==y)return x; for(i=18;i>=0;i--){ if(f[x][i]!=f[y][i]){ x=f[x][i];y=f[y][i]; } } return f[x][0]; } int main() { int n,m,i,j,q,s,c,d,g,k,u,v; while(scanf("%d%d%d",&n,&q,&s)!=EOF) { memset(first,-1,sizeof(first)); for(i=1;i<=n-1;i++){ scanf("%d%d%d",&c,&d,&g); e[i].len=g;e[i].next=first[c];e[i].to=d;e[i].from=c; first[c]=i; e[i+n-1].len=g;e[i+n-1].next=first[d];e[i+n-1].to=c;e[i+n-1].from=d; first[d]=i+n-1; } memset(b,0,sizeof(b)); dep[0]=0; tot=1; dfs(1,0); for(k=1;k<=18;k++){ for(i=1;i<=n;i++){ f[i][k]=f[f[i][k-1]][k-1]; } } for(c=1;c<=n-1;c++){ u=e[c].from;v=e[c].to; if(pre[v]==u){ swap(u,v); } update(st[u],e[c].len); //这里只要更新深度更深的点的子树就行。 update(ed[u]+1,-e[c].len); } for(i=1;i<=q;i++){ scanf("%d",&g); if(g==0){ scanf("%d",&c); int t=lca(s,c); printf("%d\n",getsum(st[s])+getsum(st[c])-2*getsum(st[t]) ); s=c; } else{ scanf("%d%d",&c,&d); u=e[c].from;v=e[c].to; if(pre[v]==u){ swap(u,v); } int cha=d-e[c].len; e[c].len=e[c+n-1].len=d; update(st[u],cha); update(ed[u]+1,-cha); } } } return 0; }