COGS2434 暗之链锁

【题目描述】


传说中的暗之连锁被人们称为Dark。Dark是人类内心的黑暗的产物,古今中外的勇者们都试图打倒它。经过研究,你发现Dark呈现无向图的结构,图中有N个节点和两类边,一类边被称为主要边,而另一类被称为附加边。Dark有N – 1条主要边,并且Dark的任意两个节点之间都存在一条只由主要边构成的路径。另外,Dark还有M条附加边。

你的任务是把Dark斩为不连通的两部分。一开始Dark的附加边都处于无敌状态,你只能选择一条主要边切断。一旦你切断了一条主要边,Dark就会进入防御模式,主要边会变为无敌的而附加边可以被切断。但是你的能力只能再切断

Dark的一条附加边。现在你想要知道,一共有多少种方案可以击败Dark。注意,就算你第一步切断主要边之后就已经把Dark斩为两截,你也需要切断一条附加边才算击败了Dark。


【输入格式】


第一行包含两个整数N和M。

之后N – 1行,每行包括两个整数A和B,表示A和B之间有一条主要边。

之后M行以同样的格式给出附加边。


【输出格式】

输出一个整数表示答案。

【样例输入】

4 1

1 2

2 3

1 4

3 4

【样例输出】

3

【提示】

自己瞎做吧

【数据范围】


对于20% 的数据,N≤100,M≤100。

对于100% 的数据,N≤100 000,M≤200 000。数据保证答案不超过2^31– 1。


我们定义w[i]为第i个节点被虚边覆盖的次数(边权已下放),那么如果w[i]=1,Ans+=1,w[i]=0,Ans+=m;

那么如何快速的求出w[i]?

可以考虑用树上差分,对于虚边(u,v),对w[u]++,w[v]++,w[lca(u,v)]-=2;

然后再进行一次dfs即可求出,那么正确性呢。

对于(u,v)下面的点肯定没有影响,对于(u,lca(u,v))和(v,lca(u,v))上的点的权值增加1,到了lca(u,v)的地方权值恢复正常。

那么就是树上差分了。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int n,m,first[MAXN],e=1,deep[MAXN],w[MAXN],f[MAXN][18];
long long Ans = 0;
 
 
template<typename _t>
inline _t read(){
	_t x=0,f=1;
	char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar())if(ch=='-')f=-f;
	for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+(ch^48);
	return x*f;
}
 
struct edge{
	int u,v,next;
}a[MAXN<<1];
 
void push(int u,int v){
	a[e].u=u;
	a[e].v=v;
	a[e].next=first[u];
	first[u]=e++;
}
 
void dfs(int u,int fa){
	deep[u]=deep[fa]+1;
	f[u][0]=fa;
	for(int i=1;i<=17;i++)
		f[u][i]=f[f[u][i-1]][i-1];
	for(int i=first[u];i;i=a[i].next)
		if(a[i].v!=fa)
			dfs(a[i].v,u);
}
 
inline int lca(int u,int v){
	if(deep[u]<deep[v])swap(u,v);
	int t = deep[u]-deep[v];
	for(int i=0;i<=17;i++)
		if(t&(1<<i))
			u=f[u][i];
	if(u==v)return u;
	for(int i=17;i>=0;i--)
		if(f[u][i]!=f[v][i])
			u=f[u][i],v=f[v][i];
	return f[u][0];
}
 
void __dfs(int u){
	for(int i=first[u];i;i=a[i].next)
		if(a[i].v!=f[u][0])
			__dfs(a[i].v),w[u]+=w[a[i].v];
}
 
int main(){
	//freopen("yam.in","r",stdin);
	//freopen("yam.out","w",stdout);
	n=read<int>();m=read<int>();
	for(int i=1;i<n;i++){
		int u=read<int>();
		int v=read<int>();
		push(u,v);push(v,u);
	}
	dfs(1,0);
	for(int i=1;i<=m;i++){
		int u=read<int>();
		int v=read<int>();
		int Lca=lca(u,v);
		w[Lca]-=2;
		w[u]++;w[v]++;
	}
	__dfs(1);
	for(int i=2;i<=n;i++){
		if(w[i]==0)Ans+=m;
		if(w[i]==1)Ans++;
	}
	printf("%lld\n",Ans);
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值