好久好久都没有发博客了!
不是因为我懒,就是集训的时候腾不出时间来写,真是不好意思!
集训期间没有发博客的类型题,很快就都会补上!
先发一篇练练手!
点分治板子题qwq
我一开始写的是每一次统计calc时 n2 暴力枚举,但是后来看题解,发现并不需要,因为一个数%3后就只有三种情况,我们可以直接根据这三种情况的个数来累加答案。
并且这道题和往常的点分治不一样,a到b的路径和b到a的路径并不重复,并且两个人同时选择一个点也是有可能的,并且要最简(我一开始都没看于是WA了……)
嗯嗯上代码:
#include<cstdio> #include<iostream> #include<queue> #include<algorithm> #include<cstring> using namespace std; #define maxn 220010 int head[maxn],nxt[maxn],to[maxn],w[maxn],dis[maxn],f[maxn],sz[maxn],num[3]; bool vis[maxn]; int cnt,n,tot,root,sum,ans; void add(int a,int b,int c) { to[++cnt]=b; nxt[cnt]=head[a]; head[a]=cnt; w[cnt]=c; } void find_root(int u,int fa) { sz[u]=1; f[u]=0; for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(v==fa||vis[v]) continue; find_root(v,u); sz[u]+=sz[v]; f[u]=max(f[u],sz[v]); } f[u]=max(f[u],sum-sz[u]); if(f[root]>f[u]) root=u; } void dfs(int u,int fa,int nowdis) { dis[++tot]=nowdis%3; num[dis[tot]]++; for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(vis[v]||v==fa) continue; dfs(v,u,nowdis+w[i]); } } int calc(int u,int nowdis) { tot=0; num[0]=num[1]=num[2]=0; dfs(u,0,nowdis%3); return num[0]*num[0]+num[1]*num[2]*2; } void devide(int u) { vis[u]=1; ans+=calc(u,0); for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(vis[v]) continue; ans-=calc(v,w[i]); root=0,f[0]=n,sum=sz[v]; find_root(v,0); devide(root); } } int gcd(int a,int b) { if(b==0) return a; return gcd(b,a%b); } int main() { scanf("%d",&n); for(int i=1;i<=n-1;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } f[0]=sum=n; find_root(1,0); devide(root); int Min=gcd(ans,n*n); printf("%d/%d",ans/Min,n*n/Min); return 0; }