http://hihocoder.com/contest/icpcbeijing2017/problem/9
求每种颜色平均出现的次数
首先模拟一轮1-n,x点最后的颜色记为fax 这样意味着下一次1-n,点x的颜色取决于当前fax的颜色
然后会发现这是一个环套树,我们首先获得一个联通块内环的大小,要知道只有环上的颜色有用,并且贡献都一样,因为只有环上的颜色会一直打转,其他地方的颜色会被覆盖,而且这个联通块的循环节是 n*环的大小,考虑某一个1-n的轮回,如果a点颜色是1,那么与a点距离是环的大小的整数倍的点,都是1,这样对联通块做了一个分割,当我们考虑循环节是n*环的大小时,颜色1会在每个联通块的点都出现一次,不重复不遗漏,这样我们把(n*环的大小)拍扁了成了n次操作,
考虑1-n轮时的一个点的一条边,如果u,v是同一个联通块的,那么其实对答案统计是没用影响的,假设u,v是同一联通块的颜色1,2,如果当前操作把颜色2染成1,那么总有一个时刻颜色1会在v上被染成同一联通块的颜色,总和其实没有变化,所以我们着眼与不同联通块之间的操作,当一个联通块因为当前操作少了一个点的时候,我们要处理出来这个联通块last与now时间内的正确之,就是 经过时间*每个时间的点树=这段时间的总点数,用last与nowsize维护一下总点数就行了
#include <iostream> #include <stdio.h> #include <cstring> #include <algorithm> #include <vector> #include <map> #include <stack> using namespace std; typedef long long int ll; const int maxn = 1e5+9; int fa[maxn],vis[maxn]; int color[maxn],ccnt; int last[maxn],cnt[maxn]; int size[maxn]; ll cot[maxn]; int nowsize[maxn]; double ans[maxn]; stack<int> stak; vector<int> vv[maxn]; void cal(int now,int color){ cot[color]+=(ll)(now-last[color])*nowsize[color]; last[color] = now; } int main(int argc, const char * argv[]) { int n,m; while (~scanf("%d%d",&n,&m)) { for(int i=1;i<=n;i++){ vv[i].clear(); fa[i] = i; vis[i] = 0; color[i] = 0; last[i] = 0; cnt[i] = 0; cot[i] = 0; nowsize[i] = 0; } ccnt = 0; for(int i=1;i<=m;i++){ int u,v; scanf("%d%d",&u,&v); vv[u].push_back(v); vv[v].push_back(u); } for(int i=1;i<=n;i++){ for(int v: vv[i]){ fa[v] = fa[i]; } } for(int i=1;i<=n;i++){ if(vis[i]==0){ int x = i; while (!stak.empty()) { stak.pop(); } while (vis[x]==0) { stak.push(x); vis[x] = 1; if(vis[fa[x]]==0) x = fa[x]; } x = fa[x]; if(color[x]!=0) { while (!stak.empty()) { int t = stak.top(); color[t] = color[x]; stak.pop(); } }else{ ccnt++; size[ccnt] = 1; while (stak.top()!=x) { size[ccnt]++; int t = stak.top(); color[t] = ccnt; stak.pop(); } while (!stak.empty()) { int t = stak.top(); color[t] = ccnt; stak.pop(); } } } } for(int i=1;i<=n;i++){ nowsize[color[i]]++; } for(int i=1;i<=n;i++){ for(int v: vv[i]){ if(color[i]!=color[v]){ cal(i-1, color[i]); cal(i-1, color[v]); nowsize[color[i]]++; nowsize[color[v]]--; color[v] = color[i]; } } } for(int i=1;i<=ccnt;i++){ cal(n, i); } int pp = 0; for(int i=1;i<=ccnt;i++){ for(int j=0;j<size[i];j++){ ans[pp++] =cot[i] *1.0/n/size[i]; } } sort(ans,ans+pp); for(int i=pp-1;i>=0;i--){ printf("%.6f\n",ans[i]); } } return 0; } /* 6 6 1 3 3 5 5 2 2 4 4 6 6 1 */