天天爱跑步 (NOIP2016)LCA+桶

并没有参加NOIP2016,不过仍然听说过这题的威名,今日一见,果然不凡操蛋

暴力分给的很足,但是暴力写完了,离正解也不远了

25 分

维护一下父亲,每条路径暴力跳就好了

40 分

对于链式,我们思考一下,怎么跑才会产生贡献
对于从上往下跑的,只有 d i s t ( s t a r t i , x ) = = W x dist(start_i,x)==W_x dist(starti,x)==Wx才可以被看到
稍微改改式子 d e p x − d e p s t a r t i = = W x — — — — > d e p s t a r t i = = d e p x − W x dep_x-dep_{start_i}==W_x————>dep_{start_i}==dep_x-Wx depxdepstarti==Wx>depstarti==depxWx
那么我们建立一个桶,遇到路径头就 B [ d e p x − W x ] + + B[dep_x-W_x]++ B[depxWx]++,遇到路径尾就 B [ d e p X − W x ] − − B[dep_X-W_x]-- B[depXWx],这样从上往下的路径就好了,那么从下往上呢,稍微推一推式子,就可以发现,这是产生贡献的东西为 d e p x + W x dep_x+W_x depx+Wx,我们我们遇到路径尾 + + ++ ++,再在路径头 − − -- ,就可以解决这个问题

60 分

这个部分头都是路径头都是1,也就是根,那么直接统计,对于每个路径,在树上查分,如果这个观察员的 d e p x = = W x + 1 dep_x==W_x+1 depx==Wx+1,答案就是差分统计的值,不然就是0

80 分

这里牵扯到了一个和正解有关的思想
我们思考一下,仍然使用桶的思想,但是会不会出现不合法的情况呢
如果两个点(点A,点B)在一棵子树中,他俩的父亲是起点,路径为 F A — — > A F_A——>A FA>A,但是如果数据巧妙, d e p x + W x dep_x+W_x depx+Wx也有可能满足统计的情况,怎么办呢,我们从下往上统计,遇到路径尾就往桶里面扔,遇到头就退掉,这样,在return回这个点的时候,我们产生的差值就是这个点被经过的贡献,正解也用到了这个思想。

100 分

终于到了满分思想
首先,我们把路径拆成两条,一个是 u — — > l c a u——>lca u>lca,一个是 l c a — — > v lca——>v lca>v,对于往上的一条,我们使用一个桶维护,详情见40分思路,对于向下的,我们使用另外一个桶,按照80分的思路维护,就可以得出结果

思路已经结束,但实际过程中会有很多问题

Q1:

统计两次LCA导致答案变大

A1:

因为路径拆成了两条,所以统计两次LCA是有可能的,我们思考一下何种情况会产生
当且仅当 d e p l c a u , v + W l c a u , v = = d e p u dep_{lca_{u,v}}+W_{lca_{u,v}}==dep_u deplcau,v+Wlcau,v==depu,这种情况下,往上的一条,该路径条件符合,会统计一次,在往下的一条,该点仍然符合条件,会被重复统计一次,这种情况,我们只需要在读入路径的时候先减一即可

Q2:

桶2负下标问题

A2:

这个直接平移数组即可

Q3

一个点是多条路径的起点(终点)怎么办

A3

我们使用链式前向星的方式,存起来就行

说了这么多,代码仍然不怎么好写,细节看代码吧

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 3e5+7;
const int N = 25;
struct node
{
	int to,next;
}edge1[maxn*2],edge2[maxn*2],edge3[maxn*2];
int cnt1,cnt2,cnt3,head1[maxn],head2[maxn],head3[maxn],ans[maxn];
int n,m,w[maxn],dep[maxn],dis[maxn],len[maxn],be[maxn],en[maxn];
int sum[maxn],b1[maxn*3],b2[maxn*3],f[maxn][N+2],mi[N+2],mov=maxn;
void add1(int from,int to)
{
	edge1[++cnt1].to=to;
	edge1[cnt1].next=head1[from];
	head1[from]=cnt1;
}
void add2(int from,int to)
{
	edge2[++cnt2].to=to;
	edge2[cnt2].next=head2[from];
	head2[from]=cnt2;
}
void add3(int from,int to)
{
	edge3[++cnt3].to=to;
	edge3[cnt3].next=head3[from];
	head3[from]=cnt3;
}

void dfs1(int u)
{
	dep[u]=dep[f[u][0]]+1;
	for(int i=1;mi[i]<dep[u];i++)f[u][i]=f[f[u][i-1]][i-1];
	for(int i=head1[u];i;i=edge1[i].next)
	{
		int to=edge1[i].to;
		if(to==f[u][0])continue;
		f[to][0]=u;
		dis[to]=dis[u]+1;
		dfs1(to);
	}
}

int lca(int a,int b)
{
	if(dep[a]>dep[b])swap(a,b);
	for(int i=log(dep[b])/log(2);i>=0;i--)
	{
		if(dep[f[b][i]]>=dep[a])
		b=f[b][i];
	}
	if(a==b)return a;
	for(int i=log(dep[a])/log(2);i>=0;i--)
	{
		if(f[a][i]!=f[b][i])
		{
			a=f[a][i];
			b=f[b][i];
		}
	}
	return f[a][0];
}


void dfs2(int u)
{
	int t1=b1[dep[u]+w[u]],t2=b2[w[u]-dep[u]+mov];
	for(int i=head1[u];i;i=edge1[i].next)
	{
		int to=edge1[i].to;
		if(to==f[u][0])continue;
		dfs2(to);
	}
	b1[dep[u]]+=sum[u];
	for(int i=head2[u];i;i=edge2[i].next) 
	{
		int to=edge2[i].to;
		b2[len[to]-dep[en[to]]+mov]++;
	}
	
	ans[u]+=b1[dep[u]+w[u]]-t1+b2[w[u]-dep[u]+mov]-t2;
	
	for(int i=head3[u];i;i=edge3[i].next)
	{
		int to=edge3[i].to;
		b1[dep[be[to]]]--;
		b2[len[to]-dep[en[to]]+mov]--;
	}
	
}

int main()
{
	mi[0]=1;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=N;i++)mi[i]=mi[i-1]*2;
	for(int i=1;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add1(x,y),add1(y,x);
	}
	for(int i=1;i<=n;i++)scanf("%d",&w[i]);
	dfs1(1);
	for(int i=1;i<=m;i++)
	{
		int x,y,Lca;
		scanf("%d%d",&x,&y);
		Lca=lca(x,y);
		be[i]=x,en[i]=y;
		len[i]=dis[x]+dis[y]-2*dis[Lca];
		add2(y,i),add3(Lca,i);
		sum[x]++;
		if(dep[Lca]+w[Lca]==dep[x])ans[Lca]--;
	}
	dfs2(1);
	for(int i=1;i<=n;i++)printf("%d ",ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值