CF191C Fools and Roads

Problem

  • 有一颗 nn个节点的树,k 次旅行,问每一条边被走过的次数。

Solution

  • 优化到最后才发现是个树上差分。。。
  • 树上差分:对于一条树链,u和v+1,lca的父亲(本题里面是lca)-2。
  • 本题相当于离线,如果修改和访问交替就需要树链剖分了。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int maxn=1e5+5;
int n,k,cnt,last[maxn],dep[maxn],logg[maxn*4],f[maxn][30],ft[maxn];
int num[maxn],ans[maxn],ac[maxn];
vector<int>ceng[maxn];
struct edge{
	int v,next;
}e[maxn<<1];
void add(int u,int v)
{
	e[++cnt].v=v;
	e[cnt].next=last[u];
	last[u]=cnt;
}
void dfs1(int x,int fa)
{
	for(int i=1;i<=20;i++)
	f[x][i]=f[f[x][i-1]][i-1];
	for(int i=last[x];i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==fa) continue;
		dep[v]=dep[x]+1;
		ceng[dep[v]].push_back(v);
		f[v][0]=x;
		ft[v]=x;
		num[v]=i/2+i%2;
		dfs1(v,x);
	}
}
int lca(int u,int v)
{
	if(dep[u]<dep[v]) swap(u,v);
	while(dep[u]!=dep[v])
	u=f[u][logg[dep[u]-dep[v]]];
	if(u==v)
	return u;
	for(int i=20;i>=0;i--)
	if(f[u][i]!=f[v][i])
	{
		u=f[u][i];
		v=f[v][i];
	}
	return f[u][0];
}
int main()
{
	for(int i=1;i<=100005;i++)
	logg[i]=logg[i-1]+((1<<(logg[i-1]+1))==i);
	cin>>n;
	for(int i=1,u,v;i<n;i++)
	{
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	cin>>k;
	dfs1(1,0);
	for(int i=1,u,v;i<=k;i++)
	{
		scanf("%d%d",&u,&v);
		ac[u]++;
		ac[v]++;
		ac[lca(u,v)]-=2;
	}	
	for(int i=100000;i>=0;i--)
	{
		for(int j=0;j<ceng[i].size();j++)
		{
			int u=ceng[i][j];
			ans[num[u]]+=ac[u];
			ac[ft[u]]+=ac[u];
		}
	}
	cout<<ans[1];
	for(int i=2;i<n;i++)
	cout<<' '<<ans[i];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈希表扁豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值