NOIP2016天天愛跑步解題思路


算法:LCA,樹上差分+(亂搞)

 如果有寫錯的地方請大佬更正

對於100%數據:

u表示起點,v表示終點

對於一條u到v的路徑,先討論LCA!=u&&LCA!=v的情況:

分為u到LCA的路徑和LCA到v的路徑

對於u到LCA的路徑上的點x,當deep[u]-deep[x]=w[x]時,即w[x]+deep[x]=deep[u]時,這條路徑對點x有貢獻;

觀察發現w[x]+deep[x]是定值,所以統計經過x的路徑中,deep[u]=w[x]+deep[x]的路徑條數。

對於LCA到v的路徑上的點x,當deep[u]-2*deep[LCA]+deep[x]=w[x]時,即w[x]-deep[x]=deep[u]-2*deep[lca]時,這條路徑對點x有貢獻;

觀察發現w[x]-deep[x]是定值,所以統計經過x的路徑中,deep[u]-2*deep[lca]=w[x]-deep[x]的路徑條數;

接下來就是統計路徑條數了,用到樹上差分

我們統計的起點(終點)一定在點x子樹內,所以統計x子樹內有多少起點(終點)的值等於所需值

即統計有多少個在點x子樹內的起點的deep[u]的值與deep[x]+w[x]相同

有多少終點的deep[u]-2*deep[lca]與w[x]-deep[x]相同

對於一個值,再u、v上加一個表示這個值+1的標記

考慮到x子樹內的路徑不一定經過x,所以在father[LCA]上加一個標記表示這個值-1

標記用動態數組儲存

然后一遍dfs用兩個桶分別統計,統計時值統一加上n,因為可能出現負數

記錄下dfs到父親節點時自己(也就是父親的兒子)所需值的個數,然后統計完子樹的值之后再做差計算自己

對於LCA==u||LCA==v的情況歸於以上兩類計算,特殊處理一下

另外,對於分裂成兩條鏈LCA可能會被統計兩遍,最后特殊判斷一下,如果被統計了兩遍就減去一遍,

復雜度:

LCA O(mlogn)

dfs統計 O(n)

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 300009
using namespace std;
int n,m;
vector<int>G[N];
int W[N];
int S[N],T[N],LCA[N];

int father[N],son[N],depth[N];
int heavyson[N],top[N];
int dfs1(int now,int fa){
	father[now]=fa;
	son[now]=1;
	depth[now]=depth[fa]+1;
	for(int i=0;i<G[now].size();++i){
		if(G[now][i]!=fa){
			dfs1(G[now][i],now);
			son[now]+=son[G[now][i]];
			if(son[G[now][i]]>son[heavyson[now]])heavyson[now]=G[now][i];
		}
	}
}

int dfs2(int now,int first){
	top[now]=first;
	if(!heavyson[now])return 0;
	dfs2(heavyson[now],first);
	for(int i=0;i<G[now].size();++i){
		if(G[now][i]!=father[now]&&G[now][i]!=heavyson[now])dfs2(G[now][i],G[now][i]);
	}
}

int swap(int &a,int &b){
	int t=a;a=b;b=t;
}

int lca(int u,int v){
	int tu=top[u],tv=top[v];
	while(tu!=tv){
		if(depth[tu]<depth[tv]){
			swap(tu,tv);swap(u,v);
		}
		u=father[tu];tu=top[u];
	}
	if(depth[u]<depth[v])return u;
	else return v;
}

int cnt[N];
int T1[N+N],T2[N+N];
struct tag{
	int v,siz;
};
vector<tag>tag1[N];
vector<tag>tag2[N];
int dfs(int now,int a,int b){
	for(int i=0;i<tag1[now].size();++i){
		T1[tag1[now][i].v+N]+=tag1[now][i].siz;
	}
	for(int i=0;i<tag2[now].size();++i){
		T2[tag2[now][i].v+N]+=tag2[now][i].siz;
	}
	
	for(int i=0;i<G[now].size();++i){
		int v=G[now][i];
		if(v==father[now])continue;
		dfs(v,T1[W[v]+depth[v]+N],T2[W[v]-depth[v]+N]);
	}
	
	cnt[now]+=T1[W[now]+depth[now]+N]+T2[W[now]-depth[now]+N]-a-b;
}

int read(){
	int r=0,k=1;
	char c=getchar();
	for(;c<'0'||c>'9';c=getchar())if(c=='-')k=-1;
	for(;c>='0'&&c<='9';c=getchar())r=r*10+c-'0';
	return r*k;
}

int main(){
	n=read();m=read();
	for(int i=1;i<=n-1;++i){
		int x=read(),y=read();
		G[x].push_back(y);
		G[y].push_back(x);
	}
	for(int i=1;i<=n;++i)W[i]=read();
	for(int i=1;i<=m;++i)S[i]=read(),T[i]=read();
	dfs1(1,0),dfs2(1,1);
	for(int i=1;i<=m;++i)LCA[i]=lca(S[i],T[i]);
	
	for(int i=1;i<=m;++i){
		if(LCA[i]==T[i]){
			tag1[S[i]].push_back((tag){depth[S[i]],1});
			tag1[father[T[i]]].push_back((tag){depth[S[i]],-1});
		}else if(LCA[i]==S[i]){
			tag2[T[i]].push_back((tag){depth[S[i]]-2*depth[LCA[i]],1});
			tag2[father[S[i]]].push_back((tag){depth[S[i]]-2*depth[LCA[i]],-1});
		}else{
			if(W[LCA[i]]+depth[LCA[i]]==depth[S[i]])--cnt[LCA[i]];
			tag1[S[i]].push_back((tag){depth[S[i]],1});
			tag1[father[LCA[i]]].push_back((tag){depth[S[i]],-1});
			tag2[T[i]].push_back((tag){depth[S[i]]-2*depth[LCA[i]],1});
			tag2[father[LCA[i]]].push_back((tag){depth[S[i]]-2*depth[LCA[i]],-1});
		}
	}
	
	dfs(1,0,0);
	
	for(int i=1;i<=n;++i)printf("%d ",cnt[i]);
	
	return 0;
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM