1. 程式人生 > >bzoj 3589: 動態樹 樹鏈剖分+線段樹

bzoj 3589: 動態樹 樹鏈剖分+線段樹

題意

給出一棵樹,要求資瓷兩個操作:
操作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; }