2 solutions
-
4
用于代码对比
#include<bits/stdc++.h> #define N 500001 using namespace std; vector<int>edge[N]; int dfn[N],tot,n,q,maxn,st[20][N],lg[N]; int arr[N],fat[N],res[N]; void dfs1(int num,int p){ dfn[num]=++tot; fat[num]=p; st[0][dfn[num]]=p; for(auto i:edge[num]){ if(i==p)continue; dfs1(i,num); } } int get(int u,int v){return dfn[u]<dfn[v]?u:v;} void init(){ lg[1]=0; for(int i=2;i<N;i++){ lg[i]=lg[i>>1]+1; } for(int k=1;k<20;k++){ for(int i=1;i+(1<<k)-1<=n;i++){ st[k][i]=get(st[k-1][i],st[k-1][i+(1<<(k-1))]); } } } int lca(int u,int v){ if(u==v)return u; u=dfn[u],v=dfn[v]; if(u>v)swap(u,v); int k=lg[v-u]; return get(st[k][u+1],st[k][v-(1<<k)+1]); } void dfs2(int num,int p){ for(auto i:edge[num]){ if(i==p)continue; dfs2(i,num); arr[num]+=arr[i]; } res[num]=arr[num]; } int qst[N]; int main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>qst[i]; } for(int i=1;i<n;i++){ int u,v; cin>>u>>v; edge[u].emplace_back(v); edge[v].emplace_back(u); } dfs1(1,0); init(); for(int i=1;i<n;i++){ arr[fat[qst[i]]]++,arr[qst[i+1]]++; arr[lca(qst[i],qst[i+1])]--,arr[fat[lca(qst[i],qst[i+1])]]--; } dfs2(1,0); arr[qst[1]]++,arr[qst[n]]--; for(int i=1;i<=n;i++){ cout<<arr[i]<<endl; } }
-
2
原题解:https://www.luogu.com.cn/article/pcq4bpud ,转载是为了方便看
主要讲一讲
树上差分
顾名思义,树上差分就是在树上的差分(???)
学过树状数组(区间修改)的同学应该了解,差分具有很优秀的性质
已知原数组 a[i],设差分数组 b[i]=a[i]-a[i-1]
那么就有 a[i]=b[1]+b[2]+...+b[n]
修改也很方便,例如对于区间(p,q)同时加上x,相当于 b[p]+=n,b[q+1]-=n
接下来就是把线性的差分移到树上,
同理,已知原数组a[i]表示每个点的点权,
我们设差分数组b[i]=a[i]-sum(a[j])(j为i的每个直接相连的儿子);
不难发现,叶子节点的b[i]恰好为该点点权a[i],此外,对于任意一个节点K,
a[K]=以K为根节点的子树中所有b[i]之和。
如果我们要对树上的一条链(u,v)的点权进行修改(同时加上x),只需要:
b[u]+=x;
b[v]+=x;
b[lca(u,v)]-=x;
b[fa[lca(u,v)]]-=x;
注意为了不影响到链外的其他点权,lca(u,v)的父节点需要修改,可以自己推一下。
这是点权的情况,如果是边权,只需要把一个点到父亲的边权赋给自己当作点权,
然后当作点权的情况做就好了,细节需要处理一下。
在本题中,原数组初始都是0,所以我们只需要读入、处理,最后遍历整颗树,递
归求出a[i]就ok了
代码:
#include <bits/stdc++.h> using namespace std; int h[300000+5],fa[300000+5][25]; int vis[300000+5],lg[300000+5],dep[300000+5]; int a[300000+5],b[300000+5];//a为糖果数,b为差分数组 int m,n,i,j,x,y,tmp; struct EDGE{ int from,to,next; }; EDGE e[600000+5]; void add(int a,int b){ tmp++; e[tmp].from=a; e[tmp].to=b; e[tmp].next=h[a]; h[a]=tmp; } void dfs1(int k){ int s=h[k]; while(s!=0){ int t=e[s].to; if(t!=fa[k][0]){ fa[t][0]=k; dep[t]=dep[k]+1; dfs1(t); } s=e[s].next; } } int lca(int a,int b){ if(dep[a]<dep[b]){ int t=a; a=b; b=t; } while(dep[a]!=dep[b]){ int k=lg[dep[a]-dep[b]]-1; a=fa[a][k]; } if(a==b) return a; else{ for(j=lg[dep[a]]-1;j>=0;j--){ if(fa[a][j]!=fa[b][j]){ a=fa[a][j]; b=fa[b][j]; } } } return fa[a][0]; } void dfs2(int k){ int s=h[k]; while(s!=0){ int t=e[s].to; if(t!=fa[k][0]){ dfs2(t); b[k]+=b[t]; } s=e[s].next; } } int main(){ cin>>n; for(i=1;i<=n;i++) scanf("%d",&vis[i]); for(i=1;i<=n-1;i++){ scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs1(1); for(j=1;j<=20;j++) for(i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1]; for(i=1;i<=n;i++) lg[i]=lg[i-1]+(1<<lg[i-1]==i); a[vis[1]]++; for(i=2;i<=n;i++){ int now=lca(vis[i],vis[i-1]); b[vis[i]]++; b[vis[i-1]]++; b[now]--; b[fa[now][0]]--; a[vis[i-1]]--;//链首糖果数直接减1 } a[vis[n]]--; dfs2(1); for(i=1;i<=n;i++) cout<<a[i]+b[i]<<endl; return 0; }
明天生日写篇题解开心一下
- 1
Information
- ID
- 323
- Time
- 1000ms
- Memory
- 125MiB
- Difficulty
- 7
- Tags
- # Submissions
- 19
- Accepted
- 7
- Uploaded By