LGP2634 [国家集训队]聪聪可可

Posted Qquun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LGP2634 [国家集训队]聪聪可可相关的知识,希望对你有一定的参考价值。

LGP2634 [国家集训队]聪聪可可

思路

1.点分治,每次归并的时候,两点的距离和对\\(3\\)取余,用桶来记录出现\\(0、1、2\\)的次数,对于路径中一个点是重心一个点在子树中的路径以及两个点在同一颗子树中的路径,方案数为\\(mp[0]-(mp[1]*mp[2]+(mp[0]*(mp[0]-1) )/2)\\)。最后所有子树合并时,容斥一下每棵子树的贡献,方案数为\\((zmp[1]*zmp[2]+(zmp[0]*(zmp[0]-1) )/2)\\)。最后由于题目求的时排列而非组合,且包括自身与自身组成的排列,所以最后\\(ans=ans*2+n\\)

2.分子求出来后,分母显然为\\(n*n\\)

3.第一个方案数括号部分是减是因为要容斥掉这一部分的贡献,这一部分减去的贡献的路径,显然不是简单路径,这是我们不想要的,因为两点在同一颗子树中,所以他们的\\(LCA\\)必不是当前重心点。第二个方案数是所有的子树的贡献,包含了每个子树的第一种方案中非简单的路径,加上第二种贡献之后,由于之前每一个子树中非简单的路径已经被减掉,所以剩下的贡献只会是那些两个点在不同子树中路径的贡献。

Code:

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
const int N=2e4+10;
struct node{
    int to;
    int next;
    int dis;
}edge[N<<1];
int head[N],num_edge,u,v,_d,base;
bool st[N];
void edge_add(int from,int to,int dis){
    edge[++num_edge].next=head[from];
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    head[from]=num_edge;
}

int get_size(int u,int fa){
	if(st[u])return 0;
	int res=1;
	for(int i=head[u];i;i=edge[i].next){
		int to=edge[i].to;
		if(to==fa)continue;
		res+=get_size(to,u);
	}
	return res;
}

int get_wc(int u,int fa,int tot,int &wc){

	if(st[u])return 0;

	int res=1;
	int mx=0;

	for(int i=head[u];i;i=edge[i].next){
		int to=edge[i].to;
		if(to==fa)continue;
		int val=get_wc(to,u,tot,wc);
		res+=val;
		mx=max(mx,val);
	}
	mx=max(mx,tot-res);
	if(mx<=tot/2)wc=u;
	return res;
}
int conp=0;
int conq=0;
int mp[3],zmp[3];
void get_dist(int u,int fa,int dist,int &conp){
	if(st[u])return ;
	mp[dist%3]++;
	zmp[dist%3]++;
	for(int i=head[u];i;i=edge[i].next){
		int to=edge[i].to;
		if(to==fa)continue;
		get_dist(to,u,dist+edge[i].dis,conp);
	}
}



int cal(int u){
	if(st[u])return 0;
	get_wc(u,-1,get_size(u,-1),u);
	st[u]=true;

	int res=0;
	conq=0;
	for(int i=head[u];i;i=edge[i].next){
		int to=edge[i].to;
		mp[0]=mp[1]=mp[2]=0;	
		get_dist(to,u,edge[i].dis,conp);

		res-=(mp[1]*mp[2]);
		res-=(mp[0]*(mp[0]-1)*1ll)/2;

		res+=mp[0];
		conp=0;

	}

		
	
	res+=(zmp[1]*zmp[2]);
	res+=(zmp[0]*(zmp[0]-1)*1ll)/2;
	zmp[0]=zmp[1]=zmp[2]=0;
	for(int i=head[u];i;i=edge[i].next)res+=cal(edge[i].to);

	return res;

}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n-1;++i){
		int x,y,w;
		scanf("%d%d%d",&x,&y,&w);
		edge_add(x,y,w);
		edge_add(y,x,w);
	}

	int fz=cal(1);

	fz=fz*2+n;
	int fm=n*n;

	int gcd=__gcd(fz,fm);
	if(fz==fm){
		puts("1/1");
	}
	else{
		printf("%d/%d",fz/gcd,fm/gcd);
	}
	return 0;
}
/*
5
1 2 1
1 3 2
1 4 1
2 5 3
 */

以上是关于LGP2634 [国家集训队]聪聪可可的主要内容,如果未能解决你的问题,请参考以下文章

P2634 [国家集训队]聪聪可可

P2634 [国家集训队]聪聪可可

P2634 [国家集训队]聪聪可可

P2634 [国家集训队]聪聪可可(点分治做法)

题解:luogu P2634 [国家集训队]聪聪可可

P2634 [国家集训队]聪聪可可(树上启发式合并)