算法随笔——树上问题若干
树上问题若干
树上差分
即在树上进行差分。
例题 1 (点差分)
题意:对树上一条路径 + 1,求最大点权值。
做法
设操作 s -> t 路径,则设差分数组
操作为
d[s]++,d[t]++,d[lca(s,t)]--,d[fa[lca(s,t)]]--;
操作完后,每个点真实权值为其子树
例题二(边差分)
题意:给出一棵树和
首先先二分答案,然后将求所有长度大于
找并可以用树上差分,边权可以转化为这条边
若路径为
操作为
d[s]++,d[t]++,d[lca(s,t)]-=2;
树链剖分
例题P3178
模版。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define re register
#define int ll
#define PII pair<int,int>
int read()
{
int f=1,k=0;char c = getchar();
while(c <'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')k=(k<<1)+(k<<3)+(c^48),c=getchar();
return k*f;
}
const int N = 4e5+5;
int n,m;
vector<int> v[N];
struct node
{
int sum,tag;
}tr[N];
int a[N];
int fa[N],top[N],son[N],siz[N],dep[N],id[N],dfn[N];
int tot;
void dfs1(int x,int fat)
{
fa[x] = fat;
siz[x] = 1;
dep[x] = dep[fat] + 1;
int maxn = 0;
for (auto y :v[x])
{
if (y == fat) continue;
dfs1(y,x);
siz[x] += siz[y];
if (siz[y] > maxn) maxn = siz[y],son[x] = y; //子节点size最大的为重儿子
}
}
void dfs2(int x,int tp)
{
top[x] = tp;
dfn[x] = ++tot;
id[tot] = x;
if (son[x]) dfs2(son[x],tp); //提前遍历重儿子
for (auto y : v[x])
if (y !=fa[x] && y != son[x]) dfs2(y,y);
}
void pushdown(int k,int l,int r)
{
if (tr[k].tag)
{
int mid = l + r >> 1;
tr[k<<1].tag += tr[k].tag;
tr[k<<1|1].tag += tr[k].tag;
tr[k<<1].sum += tr[k].tag * (mid-l+1);
tr[k<<1|1].sum += tr[k].tag * (r-mid);
tr[k].tag = 0;
}
}
void pushup(int k)
{
tr[k].sum = tr[k<<1].sum + tr[k<<1|1].sum;
}
void modify(int k,int l,int r,int x,int y,int v)
{
if (x >r || y <l) return;
if (x <= l &&r <= y)
{
tr[k].sum += (r-l+1) * v;
tr[k].tag += v;
return;
}
int mid = l +r >> 1;
pushdown(k,l,r);
modify(k<<1,l,mid,x,y,v);
modify(k<<1|1,mid+1,r,x,y,v);
pushup(k);
}
int query(int k,int l,int r,int x,int y)
{
if (x > r || y <l ) return 0;
if (x <= l && r <= y) return tr[k].sum;
pushdown(k,l,r);
int mid =l +r >> 1;
return query(k<<1,l,mid,x,y) +query(k<<1|1,mid+1,r,x,y);
}
int qsum(int x,int y)
{
int ans = 0;
while (top[x] != top[y]) //top[x] == top[y] 表示在同一条链上
{
if (dep[top[x]] < dep[top[y]]) swap(x,y);
ans += query(1,1,n,dfn[top[x]],dfn[x]);
x = fa[top[x]];
}
if (dep[x] < dep[y]) swap(x,y);
ans += query(1,1,n,dfn[y],dfn[x]);
return ans;
}
signed main()
{
cin >> n >>m;
for (int i = 1;i <= n;i++) a[i] = read();
for (int i = 1;i <= n-1;i++)
{
int x = read(),y = read();
v[x].push_back(y);
v[y].push_back(x);
}
dfs1(1,0);
dfs2(1,1);
for (int i = 1;i <= n;i++) modify(1,1,n,dfn[i],dfn[i],a[i]);
for (int i = 1;i <= m;i++)
{
int op = read(),x = read();
if ( op == 1)
{
int v = read();
modify(1,1,n,dfn[x],dfn[x],v);
}
else if (op == 2)
{
int v = read();
modify(1,1,n,dfn[x],dfn[x] + siz[x] - 1,v);
}
else{
cout <<qsum(x,1) << endl;
}
}
return 0;
}
P2486
题意:树上路径推平,树上路径查询颜色段数量。
显然可以熟练剖分转化为区间问题,然后对于每个区间维护左边颜色和右边颜色和答案。
唯一需要注意的细节是树链剖分中链与链之间也需要计算贡献。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define re register
#define PII pair<int,int>
int read()
{
int f=1,k=0;char c = getchar();
while(c <'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')k=(k<<1)+(k<<3)+(c^48),c=getchar();
return k*f;
}
const int N = 4e5+5;
int top[N],dfn[N],tot,fa[N],siz[N],dep[N],son[N];
int n,q,a[N];
int id[N];
vector<int> v[N];
struct node
{
int ans,lcol,rcol,tag;
}tr[N];
void dfs1(int x,int fat)
{
fa[x] = fat;
dep[x] = dep[fat] + 1;
siz[x] = 1;
int maxn = 0;
for (auto y : v[x])
{
if (y == fat) continue;
dfs1(y,x);
siz[x] += siz[y];
if (siz[y] > maxn) maxn = siz[y],son[x] = y;
}
}
void dfs2(int x,int tp)
{
top[x] = tp;
dfn[x] = ++tot;
id[tot] = x;
if(son[x]) dfs2(son[x],tp);
for (auto y : v[x])
{
if (y == fa[x] || y == son[x]) continue;
dfs2(y,y);
}
}
void update(node &a,node q1,node q2)
{
a.lcol = q1.lcol;
a.rcol = q2.rcol;
a.ans = q1.ans + q2.ans - (q1.rcol == q2.lcol);
}
void pushdown(int k)
{
if (tr[k].tag)
{
tr[k<<1].tag = tr[k<<1|1 ].tag = tr[k].tag;
tr[k<<1].lcol = tr[k<<1].rcol = tr[k<<1|1].lcol = tr[k<<1|1].rcol = tr[k].tag;
tr[k<<1].ans = tr[k<<1|1].ans = 1;
tr[k].tag = 0;
}
}
void modify(int k,int l,int r,int x,int y,int c)
{
if (x > r || y < l) return;
if (x <= l && r <= y)
{
tr[k].ans = 1,tr[k].lcol = c,tr[k].rcol = c ,tr[k].tag = c;
return;
}
int mid = l +r >> 1;
pushdown(k);
modify(k<<1,l,mid,x,y,c);
modify(k<<1|1,mid+1,r,x,y,c);
update(tr[k],tr[k<<1],tr[k<<1|1]);
}
node kb;
node query(int k,int l,int r,int x,int y)
{
if(x <= l &&r <= y) return tr[k];
int mid = l + r >> 1;
pushdown(k);
if (x > mid) return query(k<<1|1,mid+1,r,x,y);
if (y < mid+1) return query(k<<1,l,mid,x,y);
node q1 = query(k<<1,l,mid,x,y),q2 = query(k<<1|1,mid+1,r,x,y),res;
update(res,q1,q2);
return res;
}
int qsum(int x,int y)
{
int zb = 0,yb = 0;
int ans = 0;
while (top[x] != top[y])
{
node res;
if (dep[top[x]] > dep[top[y]])
{
res = query(1,1,n,dfn[top[x]],dfn[x]);
if (res.rcol == zb || zb==0) res.ans-=(zb!=0);
zb = res.lcol;
x = fa[top[x]];
}
else{
res = query(1,1,n,dfn[top[y]],dfn[y]);
if (res.rcol == yb || yb == 0) res.ans-=(yb!=0);
yb = res.lcol;
y = fa[top[y]];
}
ans += res.ans;
}
node res;
// int ans = 0;
// cout << zb <<' ' << yb <<endl;
if (dep[x] > dep[y])
{
res = query(1,1,n,dfn[y],dfn[x]);//cout << y <<' ' << x <<endl;
if (res.rcol == zb) res.ans--;
if (res.lcol == yb) res.ans--;
// ans += res.ans;
}
else{
res = query(1,1,n,dfn[x],dfn[y]);
if (res.rcol == yb) res.ans--;
if (res.lcol == zb) res.ans--;
}
ans += res.ans;
return ans;
}
void change(int x,int y,int c)
{
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]] ) swap(x,y);
modify(1,1,n,dfn[top[x]],dfn[x],c);
x = fa[top[x]];
}
if (dfn[x] > dfn[y]) swap(x,y);
modify(1,1,n,dfn[x],dfn[y],c);
}
void build(int k,int l,int r)
{
if (l == r)
{
tr[k].ans = 1;tr[k].rcol = tr[k].lcol = a[id[l]];tr[k].tag = 0;
/// cout <<l <<' ' << a[id[l]] <<endl;
return;
}
int mid= l +r >> 1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
update(tr[k],tr[k<<1],tr[k<<1|1]);
}
int main()
{
cin >> n >> q;
for (int i =1;i <= n;i++) a[i] = read();
for (int i = 1;i < n;i++)
{
int x= read(),y = read();
v[x].push_back(y);
v[y].push_back(x);
}
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
while (q--)
{
char op;cin >> op;
int x = read(),y = read();
if (op == 'Q')
{
// cout <<top[x] << " "<<top[y] << endl;
cout << qsum(x,y) <<endl;
}
else{
int c= read();
change(x,y,c);
}
}
return 0;
}
比较考验码力。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· 2分钟学会 DeepSeek API,竟然比官方更好用!
· .NET 使用 DeepSeek R1 开发智能 AI 客户端
· DeepSeek本地性能调优
· 一文掌握DeepSeek本地部署+Page Assist浏览器插件+C#接口调用+局域网访问!全攻略