水题,时限放得非常宽,暴力DP随便套上一波register就能卡过去。
唯一的遗憾是5A。
树形DP,s[i][j]表示以i为根的子树里距i的距离%3=j的点数,f[i]表示i为根的子树内一共有多少满足条件的点对。
两重循环暴力枚举i的所有儿子,暴力转移加波register即可。
据说正解是点分治,并不会。回头学学吧(挖坑)。
#include<cstdio> #include<cstring> #include<cctype> #include<algorithm> #include<cstdlib> #define maxn 20020 using namespace std; inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-'0'; ch=getchar(); } return num*f; } struct Edge{ int next,to,val; }edge[maxn*3]; int head[maxn],num; inline void add(int from,int to,int dis){ edge[++num]=(Edge){head[from],to,dis}; head[from]=num; } long long s[maxn][3]; long long f[maxn]; int sta[maxn],top; int dis[maxn]; void build(int x,int fa){ int lim=top; s[x][0]=1; for(int i=head[x];i;i=edge[i].next){ int to=edge[i].to; if(to==fa) continue; sta[++top]=to; dis[top]=edge[i].val; build(to,x); for(int j=0;j<3;++j){ int ret=(edge[i].val+j)%3; s[x][ret]+=s[to][j]; } } for(int i=lim+1;i<=top;++i){ f[x]+=f[sta[i]]; for(register int j=i+1;j<=top;++j){ int ret=sta[i],now=sta[j]; if(ret==now) continue; for(register int k=0;k<3;++k){ int opt=(dis[i]+k+dis[j])%3; f[x]+=s[ret][k]*s[now][(3-opt)%3]*2; } } } //f[x]*=2; f[x]+=s[x][0]+s[x][0]-1; top=lim; } inline long long gcd(long long a,long long b){ return b==0?a:gcd(b,a%b); } int main(){ int n=read(); for(int i=1;i<n;++i){ int from=read(),to=read(),dis=read(); add(from,to,dis); add(to,from,dis); } build(1,1); long long ans=f[1],ret=n*n; long long gc=gcd(ans,ret); ans/=gc;ret/=gc; printf("%lld/%lld",ans,ret); return 0; }