JZOJ3224.阴阳

题目描述
  • Farmer John 正在在计划自己的农场漫步。他的农场的结构就像一棵树:农场有N个谷仓(1<= N <=100,000),分别由N-1条路链接。这样,他便可以通过这些谷仓间的道路遍及各个谷仓。Farmer John想要选择一条路线:这条路线的起点和终点分别为农场中两个不同的谷仓,这条路线不能重复经过一条边两次。Farmer John担心这条路径可能会偏长,所以他想在路线上寻找一个休息点(当然这个休息点不能为起点或者终点)。

  • 每条边的两旁都是牛群,要么是Charcolais(白毛),要么是Angus(黑毛)。Farmer John是一个聪明人,所以他想要在他通过小路的同时平衡小路两侧阴阳的力量。他要选择一条路径使得他从起点到休息站,和从休息站到终点这两段路上都满足路两边的Charcolais牛群和Angus牛群总数量相同。

  • Farmer John好奇他能找到多少条如上所述的平衡的路径。我们认为,当且仅当两条路线的边的集合不同时,这两条路径才被认为是不同的,否则认为是相同的路线。就算路线上有多个有效的“休息站”的位置能使路线平衡,我们也只记为一条路线。

  • 请帮助计算有多少条不同的平衡路线。

数据范围

n ≤ 1 0 5 n \le 10^5 n105

题目分析
  • 这是什么智障的,难的,而且近似于模板的题目。
  • 不知道算法因为是神仙题,后来才发现这原来是点(淀)分(粉)治(质)的裸题。
  • 一般这种路径统计的题目都要想到用点分治,因为点分治不会破坏树的结构,也就不会破坏路径,保证了正确性。
点分治(简略)
  • 它其实就是不走寻常路。
  • 一般寻找路径都是直接递归到儿子然后不断往下找,但它偏要递归到本子树重心。
  • 因为重心的每棵子树大小都不超过总数的一半,所以每一次子树大小除以2,递归层数就是 O ( l o g 2 n ) O(log_2n) O(log2n)的,时间复杂度可以接受。
  • 然后就按普通统计路径的方法统计就可以了。
  • 注意点分治有个很大的特点,就是数组清空要注意不要用memset,会超时。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 999999999
using namespace std;
typedef long long ll;
const int N=100000+50,O=100000;
struct node{
	int x,y,c,next;
}a[2*N];int len,last[N],n,Len;
void ins(int x,int y,int c){
	a[++len].x=x;a[len].y=y;a[len].c=c;
	a[len].next=last[x];last[x]=len;
}
bool vis[N];int size[N],mxsn[N],rt;
int mymax(int x,int y) {return x>y?x:y;}
void getrt(int x,int fa,int Sum){
	size[x]=1;mxsn[x]=0;
	for(int k=last[x];k;k=a[k].next){
		int y=a[k].y;
		if(y==fa||vis[y]==false) continue;
		getrt(y,x,Sum);
		size[x]+=size[y];
		mxsn[x]=mymax(mxsn[x],size[y]);
	}
	mxsn[x]=mymax(mxsn[x],Sum-size[x]);
	if(mxsn[x]<mxsn[rt]) rt=x;
}
bool bz[2*N],d[N];int dis[N],B[2*N][2],h[N],G[N];ll Ans=0;
void dfs(int x,int fa){
	h[++h[0]]=x;
	if(bz[dis[x]+O]) d[x]=true;
	bool T=bz[dis[x]+O];bz[dis[x]+O]=true;
	for(int k=last[x];k;k=a[k].next){
		int y=a[k].y;
		if(vis[y]==false||y==fa) continue;
		dis[y]=dis[x]+a[k].c;dfs(y,x);
	}
	bz[dis[x]+O]=T;
}
void getans(int x){
	G[1]=0;G[0]=1;B[O][1]++;
	for(int k=last[x];k;k=a[k].next){
		int y=a[k].y;
		if(vis[y]==false) continue;
		h[0]=0;dis[y]=a[k].c;dfs(y,0);
		for(int i=1;i<=h[0];i++){
			if(dis[h[i]]==0){
				if(!d[h[i]]) Ans+=B[-dis[h[i]]+O][1]-1;
				else Ans+=B[-dis[h[i]]+O][1];
				continue;
			}
			if(!d[h[i]]) Ans+=B[-dis[h[i]]+O][0];
			else Ans+=B[-dis[h[i]]+O][1];
		}
		for(int i=1;i<=h[0];i++){
			if(!d[h[i]]) B[dis[h[i]]+O][1]++;
			else B[dis[h[i]]+O][0]++,B[dis[h[i]]+O][1]++;
			G[++G[0]]=h[i];
		}
	}
	for(int i=1;i<=G[0];i++){
		if(!d[G[i]]) B[dis[G[i]]+O][1]--;
		else B[dis[G[i]]+O][0]--,B[dis[G[i]]+O][1]--;
		d[G[i]]=false;
	}
}
void solve(int x){
	vis[x]=false;
	getans(x);
	for(int k=last[x];k;k=a[k].next){
		int y=a[k].y;
		if(vis[y]==false) continue;
		rt=0;getrt(y,0,size[x]);
		solve(rt);
	}
}
int main()
{
	scanf("%d",&n);
	len=0;memset(last,0,sizeof(last));
	for(int i=1;i<n;i++){
		int x,y,c;
		scanf("%d%d%d",&x,&y,&c);
		if(c==0) c=-1;
		ins(x,y,c);ins(y,x,c);
	}
	memset(vis,true,sizeof(vis));
	memset(bz,false,sizeof(bz));
	memset(d,false,sizeof(d));
	mxsn[0]=inf;getrt(1,0,n);solve(rt);
	printf("%lld\n",Ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值