bzoj 2152 聰聰可可
阿新 • • 發佈:2019-02-19
復雜度 using ont 計算 時間 每次 return ble ots
bzoj 2152 聰聰可可
- 類似於點分治板子那道題,但是本題的 \(n\) 更大,而在模 \(3\) 意義下計算邊權和很小,在計算子樹路徑時,可以開一個桶 \(tot[3]\) 記錄每個權值的路徑條數.
- 合並時就不用枚舉路徑了,對答案貢獻顯然直接就是 \(2*tot[1]*tot[2]+tot[0]^2\).
- 這樣做每次合並的時間復雜度為常數級,總時間復雜度為 \(O(nlogn)\).
- 這個方法(可能?)只對邊權和很小的時候適用,每次合並是邊權大小的時間復雜度.邊權較大時,若用 \(map\) 代替桶,也會多一個 \(log\) ,和暴力枚舉是同級的.
#include<bits/stdc++.h> using namespace std; #define ll long long #define mp make_pair #define pii pair<int,int> inline int read() { int x=0; bool pos=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return pos?x:-x; } #define inf 1e9 const int MAXN=2e4+10; int n; int cnt=0,head[MAXN],to[MAXN<<1],nx[MAXN<<1],val[MAXN<<1]; ll ans,tot[3]; int stk[MAXN],tp; int vis[MAXN]; int totsiz,siz[MAXN],sonsiz[MAXN],mi,rt; inline void addedge(int u,int v,int w) { ++cnt; nx[cnt]=head[u]; to[cnt]=v; val[cnt]=w; head[u]=cnt; } void ins(int u,int v,int w) { addedge(u,v,w); addedge(v,u,w); } void getroot(int u,int fa) { siz[u]=1; sonsiz[u]=0; for(int i=head[u];i;i=nx[i]) { int v=to[i]; if(vis[v] || v==fa) continue; getroot(v,u); siz[u]+=siz[v]; if(siz[v]>sonsiz[u]) sonsiz[u]=siz[v]; } sonsiz[u]=max(sonsiz[u],totsiz-siz[u]); if(sonsiz[u]<mi) mi=sonsiz[u],rt=u; } void getdis(int u,int fa,int len) { ++tot[len%3]; for(int i=head[u];i;i=nx[i]) { int v=to[i]; if(vis[v] || v==fa) continue; getdis(v,u,(len+val[i])%3); } } void solve(int rt,int len,int val) { tot[0]=tot[1]=tot[2]=0; getdis(rt,0,len); ans+=val*(2*tot[1]*tot[2]+tot[0]*tot[0]); } void Divide(int u) { vis[u]=1; solve(u,0,1); for(int i=head[u];i;i=nx[i]) { int v=to[i]; if(vis[v]) continue; solve(v,val[i],-1); mi=inf; totsiz=siz[v]; getroot(v,0); Divide(rt); } } ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; } int main() { n=read(); for(int i=1;i<n;++i) { int u=read(),v=read(),w=read(); ins(u,v,w%3); } mi=inf; totsiz=n; getroot(1,0); Divide(1); ll g=gcd(ans,1LL*n*n); printf("%lld/%lld\n",ans/g,1LL*n*n/g); return 0; }
bzoj 2152 聰聰可可