bzoj 3589: 動態樹 樹鏈剖分+線段樹
阿新 • • 發佈:2019-02-03
題意
給出一棵樹,要求資瓷兩個操作:
操作0:
這棵樹長出了一些果子, 即某個子樹中的每個節點都會長出K個果子.
操作1:
小明希望你求出幾條樹枝上的果子數. 一條樹枝其實就是一個從某個節點到根的路徑的一段. 每次小明會選定一些樹枝, 讓你求出在這些樹枝上的節點的果子數的和. 注意, 樹枝之間可能會重合, 這時重合的部分的節點的果子只要算一次.
n,m<=200000,答案模2^31-1輸出。
分析
首先這題跟lct一毛錢關係都沒有。
看到這題一個很顯然的想法就是樹鏈剖分套線段樹+容斥原理。雖然是可以過的,但是這樣複雜度較大(但實際好像跑的並不慢)。
在網上get到了一個很棒的思路:
這題套上樹鏈剖分後就變成了給你若干條線段,求這幾條線段並的權值和。
我們可以先通過樹剖把這不超過5logn條線段求出來,排序後求並,再把每段線上段樹裡面查詢就好了。
這樣的話總的複雜度就是O(5nlog^2),而且還不用求lca。
程式碼
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=200005;
int cnt,n,m,last[N],size[N],fa[N],dep[N],top[N],pos[N],sz,tot,mx[N];
struct edge{int to,next;}e[N*2];
struct tree{int s,tag;}t[N*5 ];
pair<int,int> chain[N];
int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void addedge(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last [u]=cnt;
e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}
void dfs1(int x)
{
dep[x]=dep[fa[x]]+1;size[x]=1;
for (int i=last[x];i;i=e[i].next)
{
if (e[i].to==fa[x]) continue;
fa[e[i].to]=x;
dfs1(e[i].to);
size[x]+=size[e[i].to];
}
}
void dfs2(int x,int chain)
{
pos[x]=mx[x]=++sz;top[x]=chain;int k=0;
for (int i=last[x];i;i=e[i].next)
if (e[i].to!=fa[x]&&size[e[i].to]>size[k]) k=e[i].to;
if (!k) return;
dfs2(k,chain);
for (int i=last[x];i;i=e[i].next)
if (e[i].to!=fa[x]&&e[i].to!=k) dfs2(e[i].to,e[i].to);
mx[x]=sz;
}
void pushdown(int d,int l,int r)
{
if (!t[d].tag||l==r) return;
int w=t[d].tag,mid=(l+r)/2;t[d].tag=0;
t[d*2].s+=w*(mid-l+1);t[d*2].tag+=w;
t[d*2+1].s+=w*(r-mid);t[d*2+1].tag+=w;
}
void updata(int d)
{
t[d].s=t[d*2].s+t[d*2+1].s;
}
void ins(int d,int l,int r,int x,int y,int z)
{
if (x>y) return;
pushdown(d,l,r);
if (l==x&&r==y)
{
t[d].s+=z*(r-l+1);t[d].tag+=z;
return;
}
int mid=(l+r)/2;
ins(d*2,l,mid,x,min(y,mid),z);
ins(d*2+1,mid+1,r,max(x,mid+1),y,z);
updata(d);
}
int Sig_query(int d,int l,int r,int x,int y)
{
if (x>y) return 0;
pushdown(d,l,r);
if (l==x&&r==y) return t[d].s;
int mid=(l+r)/2;
return Sig_query(d*2,l,mid,x,min(y,mid))+Sig_query(d*2+1,mid+1,r,max(x,mid+1),y);
}
void query(int x,int y)
{
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
chain[++tot]=make_pair(pos[top[x]],pos[x]);
x=fa[top[x]];
}
if (dep[x]<dep[y]) swap(x,y);
chain[++tot]=make_pair(pos[y],pos[x]);
}
bool cmp(pair<int,int> a,pair<int,int> b)
{
return a.first<b.first;
}
void solve()
{
sort(chain+1,chain+tot+1,cmp);
int ans=0,l=1,mx=chain[1].second;
for (int i=2;i<=tot;i++)
if (chain[i].first>mx) ans+=Sig_query(1,1,n,chain[l].first,mx),l=i,mx=chain[i].second;
else mx=max(mx,chain[i].second);
ans+=Sig_query(1,1,n,chain[l].first,mx);
printf("%d\n",ans&2147483647);
}
int main()
{
n=read();
for (int i=1;i<n;i++)
{
int x=read(),y=read();
addedge(x,y);
}
dfs1(1);
dfs2(1,1);
m=read();
while (m--)
{
int op=read();
if (!op)
{
int x=read(),y=read();
ins(1,1,n,pos[x],mx[x],y);
}
else
{
tot=0;
int s=read();
while (s--) query(read(),read());
solve();
}
}
return 0;
}