【题解】洛谷1600天天爱跑步(NOIP2016)

为之前一坨屎的代码向大家道歉,我发现后立即重写了一份,希望能帮助大家理解 QwQ

在这里给大家提供一种非常简单的方法。

我们先来转化一下题面:题目要求我们求每一个点在某个时间能看到的人数,那我们也可以分别计算每一个人对于他跑步的那条路径上的贡献。

那么我们可以发现一个显而易见的事情:这条路经一定由一段上行和一段下行组成。而且有且只有这两条路径。所以我们分开来看这两条路径:

  1. 如果这条路径是上行的,那么我们设这个人跑了 T T T 秒,起点深度为 d e p s t dep_{st} depst,则一定有 d e p n o w = d e p s t − T dep_{now}=dep_{st}-T depnow=depstT n o w now now 表示当前第 T T T 秒的位置)
  2. 同理,如果是下行的,那么一定有 d e p n o w = d e p s t + T dep_{now}=dep_{st}+T depnow=depst+T

移项发现: d e p s t = d e p n o w + T dep_{st}=dep_{now}+T depst=depnow+T d e p s t = d e p n o w − T dep_{st}=dep_{now}-T depst=depnowT

也就是说,对于一个起点 s t st st ,他对 d e p i + T i = d e p s t dep_i+T_i=dep_{st} depi+Ti=depst d e p i − T i = d e p s t dep_i-T_i=dep_{st} depiTi=depst 的点都会产生1的贡献。

所以我们把每一个人的路径都拆分成上行和下行两段,然后分别按照 T i + d e p i T_i+dep_i Ti+depi d e p i − T i dep_i-T_i depiTi 排序就行了。

既然我们现在处理出了每一段路径的出发点的信息,那么我们可以把观察者按照 d e p i dep_i depi 排序,然后寻找那些能够观察到的点即可。

现在第二个问题又来了:怎么对于么一个观察点,处理出所有信息与它的 d e p dep dep 相等的点呢?

这个其实很好想,因为我们其实就是在求经过这个点的路径数量,所以我们就可以在每条路径的下端点做差分,然后统计每一个观察点的字数和计可。(用 d f s dfs dfs序+树状数组统计子树和大家应该都会吧)最后记得统计完每一个信息不同的观察点后暴力将刚才插入的差分值删除就行了。复杂度: O ( n ⋅ l o g n ) O(n·log n) O(nlogn)

下面附上代码(本人亲手重写过的,体验更佳):

#include<bits/stdc++.h>
#define N 1000005
using namespace std;
int cnt[2],n,m,tot,s[N],e[N],ans[N],siz[N],dfn[N],dep[N],vis[N],t[N],sum[N],fa[N][21];
vector<int> edge[N];
struct nod{
	int p,q,r;
	nod(){;}
	nod(int _p,int _q){p=_p,q=_q;}
	nod(int _p,int _q,int _r){p=_p,q=_q,r=_r;}
}a[N],b[2][N];
stack<nod> sk;
bool cmp(nod x,nod y){return x.q<y.q;}
bool cmp2(nod x,nod y){return x.r<y.r;}
void addedge(int u,int v) {
	edge[u].push_back(v);
	edge[v].push_back(u);
}
int lowbit(int x){return x&(-x);}
void ins(int x,int v) {
	if(x==0)	return;
	for(int i=x;i<=n;i+=lowbit(i))
		sum[i]+=v;
}
int ques(int x) {
	int Ans=0;
	for(int i=x;i>0;i-=lowbit(i))
		Ans+=sum[i];
	return Ans;
}
void dfs(int x)
{
	for(int i=1;i<=20;i++)
		fa[x][i]=fa[fa[x][i-1]][i-1];
	siz[x]=vis[x]=1,dfn[x]=++tot;
	for(int i=0;i<edge[x].size();i++)
	{
		int nex=edge[x][i];
		if(!vis[nex])
		{
			fa[nex][0]=x;
			dep[nex]=dep[x]+1;
			dfs(nex),siz[x]+=siz[nex];
		}
	}
}
int lca(int x,int y)
{
	if(x==y)    return x;
	if(dep[x]<dep[y])	swap(x,y);
	for(int i=20;i>=0;i--)
		if(dep[fa[x][i]]>=dep[y])
			x=fa[x][i];
	if(x==y)    return x;
	for(int i=20;i>=0;i--)
		if(fa[x][i]!=fa[y][i])
			x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
void insedge(int st,int en,int k,int minus) {b[k][++cnt[k]]=nod(st,en,dep[k?en:st]-minus);}
int main()
{
	int u,v;
	cin>>n>>m;
	for(int i=1;i<n;i++)
		cin>>u>>v,addedge(u,v);
	for(int i=1;i<=n;i++)
		cin>>t[i];
	for(int i=1;i<=m;i++)
		cin>>s[i]>>e[i];
	dep[1]=1,dfs(1);
	for(int i=1;i<=n;i++)
		a[i]=nod(i,t[i]+dep[i]);
	for(int i=1;i<=n;i++)
		a[i+n]=nod(i,dep[i]-t[i]);
	sort(a+1,a+n+1,cmp),sort(a+n+1,a+n*2+1,cmp);

	for(int i=1;i<=m;i++)
	{
		int st=s[i],en=e[i],pre=lca(st,en);
		if(st==pre)			insedge(en,st,1,0);
		else if(en==pre)	insedge(st,en,0,0);
		else
		{
			int low=st;
			for(int i=20;i>=0;i--)
				if(dep[fa[low][i]]>dep[pre])
					low=fa[low][i];
			insedge(st,low,0,0);
			insedge(en,pre,1,dep[st]-dep[pre]);
		}
	}
	sort(b[0]+1,b[0]+cnt[0]+1,cmp2);
	sort(b[1]+1,b[1]+cnt[1]+1,cmp2);
	int x=1,y=1;

	for(int i=1;i<=n;i++)
	{
		int cur=a[i].p;
		if(a[i].q!=a[i-1].q)
			while(!sk.empty()) {
				nod now=sk.top(); sk.pop();
				ins(now.p,-1),ins(now.q,1);
			}
		for(;x<=cnt[0] && b[0][x].r<=a[i].q;x++)
			if(a[i].q!=a[i-1].q && b[0][x].r==a[i].q)
			{
				int lo=b[0][x].p,hi=b[0][x].q;
				ins(dfn[lo],1),ins(dfn[fa[hi][0]],-1);
				sk.push(nod(dfn[lo],dfn[fa[hi][0]]));
			}
		ans[cur]+=ques(dfn[cur]+siz[cur]-1)-ques(dfn[cur]-1);
	}
	while(!sk.empty()) {
		nod now=sk.top(); sk.pop();
		ins(now.p,-1),ins(now.q,1);
	}

	for(int i=n+1;i<=n*2;i++)
	{
		int cur=a[i].p;
		if(a[i].q!=a[i-1].q)
			while(!sk.empty()) {
				nod now=sk.top(); sk.pop();
				ins(now.p,-1),ins(now.q,1);
			}
		for(;y<=cnt[1] && b[1][y].r<=a[i].q;y++)
			if(a[i].q!=a[i-1].q && b[1][y].r==a[i].q)
			{
				int lo=b[1][y].p,hi=b[1][y].q;
				ins(dfn[lo],1),ins(dfn[fa[hi][0]],-1);
				sk.push(nod(dfn[lo],dfn[fa[hi][0]]));
			}
		ans[cur]+=ques(dfn[cur]+siz[cur]-1)-ques(dfn[cur]-1);
	}
	while(!sk.empty()) {
		nod now=sk.top(); sk.pop();
		ins(now.p,-1),ins(now.q,1);
	}

	for(int i=1;i<=n;i++)
		cout<<ans[i]<<" ";
	return 0;
}
  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值