【题解】CF1749F Distance to the Path
部分参考了 这篇 xianggl巨佬的博客。
由于
由于是区间更新,需要同时更新
对于在子树内的点,倘若是一段连续的区间,则可以利用数据结构一次更新完毕。不难想到利用树链剖分维护 modify (c[d],x,k);modify (c[d],y,k);modify (c[d],LCA(x,y),-2 * k),其中 modify 函数即为树状数组的操作。用 in[x] 和 out[x] 分别表示第一次和最后一次经过
对于在子树外的点,显然都可以归结到
综上分析,子树内修改为
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#define init(x) memset (x,0,sizeof (x))
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define lowbit(x) x & (-x)
using namespace std;
const int MAX = 2e5 + 5;
const int MOD = 1e9 + 7;
inline int read ();
int n,m,cnt,in[MAX],out[MAX],top[MAX],sz[MAX],hson[MAX],f[MAX],dep[MAX],dp[MAX][25],c[25][MAX];
vector <int> ve[MAX];
void dfs1 (int u,int fa);
void dfs2 (int u,int fa);
int lca (int u,int v);
void modify (int tr[],int x,int v);
int query (int tr[],int x);
int main ()
{
//freopen (".in","r",stdin);
//freopen (".out","w",stdout);
n = read ();
for (int i = 1;i < n;++i)
{
int x = read (),y = read ();
ve[x].push_back (y);ve[y].push_back (x);
}
dfs1 (1,0);
in[1] = ++cnt,top[1] = 1;
dfs2 (1,0);
//for (int i = 1;i <= n;++i) cout<<in[i]<<" "<<out[i]<<endl;
m = read ();
for (int i = 1;i <= m;++i)
{
int ty = read ();
if (ty == 1)
{
int x = read (),ans = 0,p = 0;
for (int i = 0;i <= 20;++i)
{
ans += query (c[i],out[x]) - query (c[i],in[x] - 1);//差分思想
for (int j = p;j <= 20;++j) ans += dp[x][j];//每向上走一次,距离至少为 p
++p;x = f[x];
if (!x) break;//向上面已经没有节点就停止
}
printf ("%d\n",ans);
}
else
{
int u = read (),v = read (),k = read (),d = read ();
int z = lca (u,v);
modify (c[d],in[u],k);modify (c[d],in[v],k);modify (c[d],in[z],-2 * k);//重复部分的去除
for (int i = d;~i;--i)
{
dp[z][i] += k;
if (i - 2 >= 0 && f[z]) dp[z][i - 2] -= k;//容斥 距离 >= i - 2 且 z 上还有节点
z = f[z];
if (!z) break;
}
}
}
return 0;
}
inline int read ()
{
int s = 0;int f = 1;
char ch = getchar ();
while ((ch < '0' || ch > '9') && ch != EOF)
{
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9')
{
s = s * 10 + ch - '0';
ch = getchar ();
}
return s * f;
}
void dfs1 (int u,int fa)//树链剖分
{
dep[u] = dep[fa] + 1;
f[u] = fa;sz[u] = 1;
for (auto v : ve[u])
{
if (v == fa) continue;
dfs1 (v,u);
sz[u] += sz[v];
if (sz[v] > sz[hson[u]]) hson[u] = v;
}
}
void dfs2 (int u,int fa)
{
if (hson[u])
{
in[hson[u]] = ++cnt;//dfs 序
top[hson[u]] = top[u];
dfs2 (hson[u],u);
}
for (auto v : ve[u])
{
if (top[v]) continue;
in[v] = ++cnt;
top[v] = v;
dfs2 (v,v);
}
out[u] = cnt;//dfs 序
}
int lca (int u,int v)//树链剖分求 LCA
{
int fx = top[u],fy = top[v];
while (fx != fy)
{
if (dep[fx] < dep[fy]) swap (fx,fy),swap (u,v);
u = f[fx],fx = top[u];
}
if (dep[u] > dep[v]) swap (u,v);
return u;
}
void modify (int tr[],int x,int v) {for (int i = x;i <= n;i += lowbit (i)) tr[i] += v;}
int query (int tr[],int x) {int sum = 0;for (int i = x;i;i -= lowbit (i)) sum += tr[i];return sum;}//树状数组基本操作