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
*/