听说是树分治的题,就进来了,
然而一看数据,最大才2000
n^2暴力应该能过啊;但是T了,因为——此题时限0.3秒、、
听说树形dp和点分治都可以, 那先想想吧
结果,竟然搞了一天、、、
如果学完了树分治, 其实思路是好想的:
3的倍数和具体的数字没有关系,所以只需统计dis%3的值,
因为:所有%3=1的数+%3=2的数再%3一定=0,对答案有贡献(魔法分配律)
需要递归加减的量就是 余数为2的个数*(余数为1的个数-1)+∑(i=1;i<(余数为0的个数-1))(i);
直接树分治就可以搞了。
码:
#include<iostream>
#include<cstdio>
using namespace std;
#include<cstring>
#define N 20001
#define M 40002
int tot,xia[N],d[N],zhe,hou[M],zhong[M],zhi[M],x,y,n,z,size[N],root,max1=1e9,yu[3],ans;
bool vis[N];
void add(int x,int y,int z)
{
hou[++tot]=xia[x],xia[x]=tot,zhong[tot]=y,zhi[tot]=z;
}
void dfs(int now,int fa)
{
size[now]=1;
int maxx=0;
for(int i=xia[now];i!=-1;i=hou[i])
{
int nd=zhong[i];
if(nd==fa||vis[nd])continue;
dfs(nd,now);
size[now]+=size[nd];
maxx=max(maxx,size[nd]);
}
maxx=max(maxx,tot-size[now]);
if(maxx<max1)max1=maxx,root=now;
}
void dfs2(int now,int fa)
{
yu[d[now]%3]++;
for(int i=xia[now];i!=-1;i=hou[i])
{
int nd=zhong[i];
if(vis[nd]||nd==fa)continue;
d[nd]=d[now]+zhi[i];
dfs2(nd,now);
}
}
int work(int now,int hehe)
{
yu[0]=0;
yu[1]=0;
yu[2]=0;
d[now]=hehe;
// cout<<now<<" "<<d[now]<<endl;
// zhe=now;
dfs2(now,0);
return yu[1]*yu[2]+(yu[0])*(yu[0]-1)/2;
}
//int work2(int now)
//{
// yu[0]=0;
// yu[1]=0;
// yu[2]=0;
// zhe=now;
// dfs2(now,0);
//yu[d[now]%3]++;
// // cout<<now<<" "<<d[now]<<endl;
//
// return yu[1]*yu[2]+(yu[0]+1)*yu[0]/2;
//
//}
int solve(int now)
{ //cout<<now<<" ";
ans+=work(now,0);
//cout<<now<<" "<<ans<<" ";
vis[now]=1;
for(int i=xia[now];i!=-1;i=hou[i])
{
int nd=zhong[i];
if(vis[nd])continue;
ans-=work(nd,zhi[i]);
// cout<<nd<<" "<<ans<<endl;
max1=1e9;tot=size[nd];
dfs(nd,now);
solve(root);
}
}
int gcd(int a,int b)
{
if(!b)return a;
return gcd(b,a%b);
}
int main()
{
memset(xia,-1,sizeof(xia));
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
// for(int i=xia[4];i!=-1;i=hou[i]
// {
// cout<<zhong[i]<<" ";
// }
// cout<<endl;
max1=1e9;tot=n;
dfs(1,0);
solve(root);
int hehehe=gcd(ans*2+n,n*n);
printf("%d/%d",(ans*2+n)/hehehe,n*n/hehehe);
}
可以分析一下
注意到了根节点本身也被拉到%3=0的序列中了
注意到了经过上一个根的同一子树的情况都应该减掉,直接套用原来加的函数计算即可(子树的根节点在总的根节点的距离和其他节点是同级的,所以子树的根节点也要一块算进去,不用特殊处理!!!)
另一点要注意:减的操作要在找重心前!!!
这样来看是可以保证正确的,
但如果你想闹着玩,你也可以把计算方法改成:余数为2的个数*余数为1的个数+∑(i=1;i<余数为0的个数)(i)
这也是不影响的,因为如果你把单链也算进去,减的时候的同一子树的单链也会被减掉,所以是无意义的
虽然犀利糊涂的过了,但有一点比较重要,看到上面大段注释掉的代码应该知道:
加减的函数 我当成了不一样的!!
今天是我最ZZ的一天
其实分析都不用分析,因为既然是递归,要加要减肯定是一起的。