【树上差分】【例题】Network

POJ3417

题目:

已知一棵有n个点的树,现加m条边,问:有多少种方案,选一条原树边,一条新加边,使两边删去后图不连通

题解:

乍一看,好像和割点和桥有点像,然而找到桥后就不知道怎么办了。
x->y的新加边:x->lca(x,y),y->lca(x,y)中经过边加一覆盖数
其实统计被新加边覆盖的边和覆盖次数就行了
未被覆盖: 可以与任意新加边组合成合法方案 ( a n s + = m ) (ans+=m) (ans+=m)
被覆盖一次: 仅可以与此覆盖边组成合法方案 ( a n s + + ) (ans++) (ans++)
被覆盖两次及以上:无效
———————————
大致方法掌握了,暴力加方案被覆盖数吗?
TLE了,QAQ
联想一下,我们每次加一整条路径,额,似乎和区间加有点像
我们想到了线段树维护区间值,额,这样的话,似乎要树剖一下套线段树,我还是洗洗睡吧
其实可以更简单一点,有请今天的主角:

树上差分

其实就是将数列上的操作照搬下来了
修 改 : 左 端 点 + v a l , ( 右 端 点 + 1 ) − v a l              左 节 点 + v a l , 右 节 点 + v a l , L C A − 2 ∗ v a l 修改:左端点+val,(右端点+1)-val \\ \ \ \ \ \ \ \ \ \ \ \ \ 左节点+val,右节点+val,LCA-2*val +val,(+1)val            +val,+val,LCA2val
求 值 : 前 缀 1 − > i , 表 示 i 上 的 值              前 缀 子 树 , 表 示 该 节 点 的 值 ( 当 然 也 可 以 是 当 前 边 ) 求值:前缀1->i,表示i上的值\\ \ \ \ \ \ \ \ \ \ \ \ \ 前缀子树,表示该节点的值(当然也可以是当前边) 1>i,i            ,
额,似乎就没了。。。

#include<cstdio>
#include<iostream>
#include<string.h>
#define ch getchar()
#define ir read()
inline int ir
{
	int ans=0;char s=ch;
	while(s>'9'||s<'0')s=ch;
	while(s>='0'&&s<='9')
	{
		ans=(ans*10)+(s&15);
		s=ch;
	}return ans;
}
using namespace std;
const int N=1e5+10,M=5e5+10;
int n,m,ans;
int fa[N],dep[N];
int head[N],nex[M],to[M],tot=1;
void build(int u,int v){tot++;nex[tot]=head[u];to[tot]=v;head[u]=tot;}
int bz[N][25];
void get_bz()
{
	for(int k=1;k<=20;k++)
		for(int i=1;i<=n;i++)
			bz[i][k]=bz[bz[i][k-1]][k-1];
}
int lca(int x,int y)
{
	if(dep[x]<dep[y])swap(x,y);
	for(int i=20;i>=0;i--)
		if(dep[bz[x][i]]>=dep[y])x=bz[x][i];
	if(x==y)return x;
	for(int i=20;i>=0;i--)
	{
		if(bz[x][i]!=bz[y][i])
		x=bz[x][i],y=bz[y][i];
	}return fa[x];
}
void dfs(int u,int f)
{
	bz[u][0]=fa[u]=f;
	dep[u]=dep[f]+1;
	for(int i=head[u];i;i=nex[i])
	{
		int v=to[i];
		if(v==f)continue;
		dfs(v,u);
	}
}
int num[N];
int dfs(int u,int f)
{
	int g=num[u];
	for(int i=head[u];i;i=nex[i])
	{
		int v=to[i];
		if(v==f)continue;
		g+=dfs(v,u);
	}
	if(u!=1&&g==0)ans+=m;
	else  if(g==1)ans++;
	return g;
}
int x,y;
int main()
{
	n=ir;m=ir;
	for(int i=1;i<n;i++)
	{
		x=ir;y=ir;
		build(x,y);build(y,x);
	}
	dfs(1,0);
	get_bz();
	for(int i=1;i<=m;i++)
	{
		x=ir;y=ir;;
		num[x]++;num[y]++;
		num[lca(x,y)]-=2;
	}
	dfs(1,0);
	printf("%d",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值