树链剖分
推荐博客:
https://www.cnblogs.com/ivanovcraft/p/9019090.html
这讲的很好,主要是用来处理树上路径问题的高效算法
首先明确概念:
重儿子:父亲节点的所有儿子中子树结点数目最多(size最大)的结点;
轻儿子:父亲节点中除了重儿子以外的儿子;
重边:父亲结点和重儿子连成的边;
轻边:父亲节点和轻儿子连成的边;
重链:由多条重边连接而成的路径;
轻链:由多条轻边连接而成的路径;
名称 | 解释 |
f[u] | 保存结点u的父亲节点 |
d[u] | 保存结点u的深度值 |
size[u] | 保存以u为根的子树节点个数 |
son[u] | 保存重儿子 |
rk[u] | 保存当前dfs标号在树中所对应的节点 |
top[u] | 保存当前节点所在链的顶端节点 |
id[u] | 保存树中每个节点剖分以后的新编号(DFS的执行顺序) |
题目:洛谷 https://www.luogu.org/problem/P3384
ac代码:
1 #include<bits/stdc++.h> 2 const ll MAX=100000+5; 3 ll p; 4 vector<ll>q[maxn]; 5 ll f[maxn]; 6 ll d[maxn]; 7 ll siz[maxn]; 8 ll son[maxn]; 9 10 ll top[maxn]; 11 ll id[maxn]; 12 ll rk[maxn]; 13 14 ll a[maxn]; 15 16 struct Tree 17 { 18 ll l,r,sum; 19 } tree[maxn<<2]; 20 ll dfs(ll a,ll fa) 21 { 22 f[a]=fa; 23 d[a]=d[fa]+1; 24 siz[a]=1; 25 for(ll i=0; i<q[a].size(); i++) 26 { 27 ll z=q[a][i]; 28 if(z!=fa) 29 { 30 dfs(z,a); 31 siz[a]+=siz[z]; 32 if(!son[a]||siz[z]>siz[son[a]]) 33 son[a]=z; 34 } 35 } 36 return 0; 37 } 38 ll lz[maxn]; 39 void build(ll x,ll l,ll r) 40 { 41 tree[x].l=l; 42 tree[x].r=r; 43 if(l==r) 44 tree[x].sum=a[rk[l]]; 45 if(l==r)return ; 46 ll mid=(l+r)>>1; 47 build(x<<1,l,mid); 48 build(x<<1|1,mid+1,r); 49 tree[x].sum=(tree[x<<1].sum+tree[x<<1|1].sum); 50 } 51 void pushup(ll x) 52 { 53 ll LL,rr; 54 LL=tree[x].l; 55 rr=tree[x].r; 56 ll k=lz[x]; 57 lz[x<<1]+=k; 58 lz[x<<1|1]+=k; 59 ll mid=(LL+rr)>>1; 60 tree[x<<1].sum+=(mid-LL+1)*lz[x]; 61 tree[x<<1].sum%=p; 62 tree[x<<1|1].sum+=(rr-mid)*lz[x]; 63 tree[x<<1|1].sum%=p; 64 lz[x]=0; 65 66 } 67 68 void add(ll x,ll l,ll r,ll k) 69 { 70 ll LL,rr; 71 LL=tree[x].l; 72 rr=tree[x].r; 73 if(l<=LL&&rr<=r) 74 { 75 tree[x].sum=(tree[x].sum+(rr-LL+1)*k)%p; 76 lz[x]+=k; 77 return ; 78 } 79 if(lz[x]) pushup(x); 80 ll mid=(LL+rr)>>1; 81 if(mid>=l) add(x<<1,l,r,k); 82 if(mid<r)add(x<<1|1,l,r,k); 83 tree[x].sum=(tree[x<<1].sum+tree[x<<1|1].sum)%p; 84 } 85 ll pri(ll x,ll l,ll r) 86 { 87 ll sum=0; 88 ll LL,rr; 89 LL=tree[x].l; 90 rr=tree[x].r; 91 if(l<=LL&&rr<=r) 92 { 93 return tree[x].sum; 94 } 95 if(lz[x]) pushup(x); 96 ll mid=(LL+rr)>>1; 97 if(mid>=l) sum+=pri(x<<1,l,r); 98 sum%=p; 99 if(mid<r)sum+=pri(x<<1|1,l,r); 100 return sum%p; 101 } 102 ll sum; 103 void dfs1(ll a,ll b) 104 { 105 top[a]=b; 106 id[a]=++sum; 107 rk[sum]=a; 108 if(!son[a]) 109 return ; 110 dfs1(son[a],b); 111 for(ll i=0; i<q[a].size(); i++) 112 { 113 ll z=q[a][i]; 114 if(z!=f[a]&&z!=son[a]) 115 dfs1(z,z); 116 } 117 } 118 void Q1(ll x,ll y,ll z) 119 { 120 while(top[x]!=top[y]) 121 { 122 if(d[top[x]]<d[top[y]])swap(x,y); 123 add(1,id[top[x]],id[x],z); 124 x=f[top[x]]; 125 } 126 if(d[x]>d[y])swap(x,y); 127 add(1,id[x],id[y],z); 128 } 129 ll Q2(ll x,ll y) 130 { 131 ll sum=0; 132 while(top[x]!=top[y]) 133 { 134 if(d[top[x]]<d[top[y]])swap(x,y); 135 sum=(sum+pri(1,id[top[x]],id[x]))%p; 136 x=f[top[x]]; 137 } 138 if(d[x]>d[y])swap(x,y); 139 sum=(sum+pri(1,id[x],id[y]))%p; 140 return (sum+p)%p; 141 } 142 void Q3(ll x,ll z) 143 { 144 add(1,id[x],id[x]+siz[x]-1,z); 145 } 146 int main() 147 { 148 // ios::sync_with_stdio(false); 149 ll n,m,r; 150 scanf("%lld%lld%lld%lld",&n,&m,&r,&p); 151 for(ll i=1; i<=n; i++) 152 scanf("%lld",&a[i]); 153 for(ll i=1; i<n; i++) 154 { 155 ll x,y; 156 scanf("%lld%lld",&x,&y); 157 q[x].pb(y); 158 q[y].pb(x); 159 } 160 dfs(r,0); 161 dfs1(r,r); 162 build(1,1,n); 163 while(m--) 164 { 165 ll a; 166 cin>>a; 167 if(a==1) 168 { 169 ll x,y,z; 170 scanf("%lld%lld%lld",&x,&y,&z);z%=p; 171 Q1(x,y,z); 172 } 173 else if(a==2) 174 { 175 ll x,y; 176 cin>>x>>y; 177 cout<<Q2(x,y)<<endl; 178 } 179 else if(a==3) 180 { 181 ll x,z;z%=p; 182 cin>>x>>z; 183 Q3(x,z); 184 } 185 else 186 { 187 ll x; 188 cin>>x; 189 cout<<(pri(1,id[x],id[x]+siz[x]-1)+p)%p<<endl; 190 } 191 } 192 }