【NOIP2016】【桶/線段樹合併】【樹上差分】天天愛跑步

【題目描述】
在這裏插入圖片描述

【思路】

這是道好題呀。考慮把一條路徑(u,v)拆成兩條:從u到lca(u,v),從lca(u,v)到v。下面我們以向上的路徑爲例討論做法。對於一條向上的路徑,它對一個點x有貢獻當且僅當它覆蓋了點x且dep[u]dep[x]=w[x]dep[u]-dep[x]=w[x]。即對於一個點x,我們需要統計有多少覆蓋了它的路徑的起點u滿足w[x]+dep[x]=dep[u]w[x]+dep[x]=dep[u]。顯然後面這個限制只需要維護一個數據結構就行了。那麼問題在於如何使它只統計了覆蓋自己的路徑。注意到覆蓋父親和兒子的邊只有一部分不同,我們可以考慮父親繼承子樹信息,然後刪除其中沒有覆蓋自己的路徑。對於路徑的刪除和加入,我們可以用樹上差分實現,在u處加入該邊,在fa[lca(u,v)]處刪除該邊。每條路徑最多被加入一次,刪除一次,所以時間複雜度正確。可以使用線段樹合併實現父親繼承子樹信息。但是考慮到這是計數問題,並非最優化問題,答案滿足可減性。所以我們不需要線段樹合併保證只考慮了子樹內的路徑。我們可以在處理子樹之前查詢一次答案,在處理子樹之後查詢一次答案,兩次答案的變化量就是子樹中的路徑對自己的貢獻。這樣使用一個桶就可以實現上述操作。我們按dfs的順序依次處理每個點:
1.查詢一次答案ans1ans_1
2.遞歸處理子樹。
3.處理自己。
4.查詢一次答案ans2ans_2,這個點的答案即ans2ans1ans_2-ans_1

對於向下的路徑依然可以類似操作,只需要查詢滿足dep[u]+dep[x]2dep[lca(u,v)]=w[x]dep[u]+dep[x]-2*dep[lca(u,v)]=w[x]的路徑數量,移項即可維護。同樣,我們在v處向桶中加入這條路徑,在fa[lca(u,v)]刪除這條路徑即可。注意,爲防止lca處重複統計了兩條路徑的貢獻,我們可以選擇一條路徑在lca處刪除,另一條在fa[lca]處刪除。

代碼:

#include<bits/stdc++.h>
#define re register
#define F(i,a,b) for(int re i=a;i<=b;++i)
#define D(i,a,b) for(int re i=a;i>=b;--i)
using namespace std;
const int N=6e5+5,M=3e5+5;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int red(){
    char re ch=nc();int re sum=0;
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=((sum<<2)+sum<<1)+(ch^48),ch=nc();
    return sum;
}
int n,m,a,b;
int to[N],f[M],nxp[N],cnt=0;
inline void add(int u,int v){
	to[++cnt]=v;nxp[cnt]=f[u];f[u]=cnt;
	to[++cnt]=u;nxp[cnt]=f[v];f[v]=cnt; 
}
struct que{int pos,v;que(int x=0,int y=0){pos=x,v=y;}};
vector<que>g[2][M];
int fa[M],dep[M],top[M],son[M],ans[M],w[M],siz[M],num[2][N];
void dfs1(int u){int v,&x=son[u];
	siz[u]=1;dep[u]=dep[fa[u]]+1;
	for(int i=f[u];i;i=nxp[i]){
		if((v=to[i])==fa[u])continue;
		fa[v]=u;dfs1(v);siz[u]+=siz[v];
		if(siz[x]<siz[v])x=v;
	}
}
void dfs2(int u){
	if(son[u])top[son[u]]=top[u],dfs2(son[u]);
	for(int i=f[u];i;i=nxp[i])
		if(!top[to[i]])dfs2(top[to[i]]=to[i]);
}
inline int lca(int a,int b){
	while(top[a]^top[b])dep[top[a]]<dep[top[b]]?b=fa[top[b]]:a=fa[top[a]];
	return dep[a]<dep[b]?a:b;
}
inline int calc(int u){return num[0][w[u]+dep[u]]+num[1][w[u]-dep[u]+M];}
inline void insert(int u,int op){D(i,g[op][u].size()-1,0)num[op][g[op][u][i].pos]+=g[op][u][i].v;}
void dfs3(int u){
	int ans1=calc(u);
	for(int re i=f[u];i;i=nxp[i])if(dep[to[i]]>dep[u])dfs3(to[i]);
	insert(u,0);insert(u,1);
	ans[u]=calc(u)-ans1;
}
inline void print(int x){
	if(x>9)print(x/10);
	putchar(x%10^48);
}
int main()
{
	n=red();m=red();int rt=1;
	F(i,2,n)add(red(),red());
	F(i,1,n)w[i]=red();dfs1(rt),dfs2(top[rt]=rt);
	F(i,1,m){
		int u=red(),v=red(),x=lca(u,v),y=fa[x];
		g[0][u].push_back(que(dep[u],1));
		g[1][v].push_back(que(dep[u]-2*dep[x]+M,1));
		g[0][y].push_back(que(dep[u],-1));
		g[1][x].push_back(que(dep[u]-2*dep[x]+M,-1));
	}dfs3(rt);
	F(i,1,n)print(ans[i]),putchar(' ');
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章