[NOIP提高组2016]天天爱跑步

这一道题是一道典型的由部分分推正解的题。

对于子链,所有si=1,所有ti=1的情况,经过思考,会发现正解只需要将所有的路径拆成两条链,两种情况分别跑一次,把结果加起来即可。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#define il inline
#define dg isdigit
#define gc getchar()
using namespace std;
template<typename T>il void read(T &x){
	x=0;int f=0;char s=gc;
	while(!dg(s))f|=s=='-',s=gc;
	while( dg(s))x=x*10+s-48,s=gc;
	x=f?-x:x;
}
const int N=3e5+10;
struct edge{
	int v,nxt;
}e[N<<1];int tot,last[N];
il void Addedge(int u,int v){
	e[++tot]=(edge){v,last[u]};
	e[++tot]=(edge){u,last[v]};
	last[u]=tot-1,last[v]=tot;
}
int n,m,w[N],d[N];
int fa[N],ff[N],vis[N],lca[N];
vector<int>query[N],query_id[N];
il void Addquery(int u,int v,int id){
	query[u].push_back(v),query_id[u].push_back(id);
	query[v].push_back(u),query_id[v].push_back(id);
}
int findfa(int x){
	return x==fa[x]?x:fa[x]=findfa(fa[x]);
}
struct node{
	int x,y;
}b[N];
void Tarjan(int u){
	vis[u]=1;
	for(int i=last[u];i;i=e[i].nxt){
		int v=e[i].v;
		if(vis[v])continue;
		d[v]=d[u]+1;
		Tarjan(v);
		fa[v]=u;ff[v]=u;
	}
	for(int i=0;i<query[u].size();i++){
		int v=query[u][i],id=query_id[u][i];
		if(vis[v]==2){
			int tt=findfa(v);
			if(d[lca[id]]<d[tt])lca[id]=tt;
		}
	}
	vis[u]=2;
}
int ans[N],c1[N<<1],c2[N<<1]; 
vector<int>a1[N],a2[N],b1[N],b2[N];
void dfs(int u){
	int val1=c1[w[u]+d[u]],val2=c2[w[u]-d[u]+n];
	for(int i=last[u];i;i=e[i].nxt){
		int v=e[i].v;
		if(v!=ff[u])dfs(v);
	}
	for(int i=0;i<a1[u].size();i++)c1[a1[u][i]]++;
	for(int i=0;i<a2[u].size();i++)c1[a2[u][i]]--;
	for(int i=0;i<b1[u].size();i++)c2[b1[u][i]+n]++;
	for(int i=0;i<b2[u].size();i++)c2[b2[u][i]+n]--;
	ans[u]+=c1[d[u] + w[u]]-val1+c2[w[u]-d[u]+n]-val2;
}
int main(){
	read(n),read(m);
	for(int i=1,x,y;i<n;i++){
		read(x),read(y);
		Addedge(x,y);
	}
	for(int i=1;i<=n;i++)read(w[i]);
	for(int i=1;i<=n;i++)
		fa[i]=i,vis[i]=0;
	for(int i=1,S,T;i<=m;i++){
		read(S),read(T);
		b[i].x=S,b[i].y=T;
		if(S==T)lca[i]=S;
		else{
			Addquery(S,T,i);
			lca[i]=0;
		}
	}
	d[1]=1;Tarjan(1);
	for(int i=1;i<=m;i++){
		int x=b[i].x,y=b[i].y,z=lca[i];
		a1[x].push_back(d[x]);
		a2[ff[z]].push_back(d[x]);
		b1[y].push_back(d[x]-2*d[z]);
		b2[z].push_back(d[x]-2*d[z]);
	}
	dfs(1);
	for(int i=1;i<=n;i++)printf("%d ",ans[i]);
	return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值