Description
Input
第一行包括两个整数N,M。
接下来M行每行两个整数u,v,代表存在一条里连接 u,v的无向边。可能存在重边自环。
Output
降序输出所有不为0的F(i) 。保留6位小数输出。
Sample Input
输入1:
5 5
1 2
2 5
3 4
4 5
3 5
输入2:
5 4
1 2
2 5
5 4
4 3
Sample Output
输出1:
3.000000
2.000000
输出2:
2.800000
2.200000
Data Constraint
对于20%的数据,n,m<=100
对于40%的数据,n,m<=5000
另外有20%的数据,保证图为一个连通的简单环,且当且仅当|u-v|=1 ,存在u到v的边。
对于100%的数据,n,m<=500000
Solution
我们可以愉快的发现这种染色很快就会进入一个循环……
听说可以证明循环的次数,可是我不会,就只做个十几二十轮。
用哈希判断一下当前状态出现过没,出现了就说明出现了循环节!
由于 F 数组的是通过取极限得到的,我们只需要保存循环节的答案即可。
统计循环节的答案时要打一下标记就能均摊 O(1) 计算了。
要注意一下精度问题~~。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
const int N=500001,mo=1e9+7,M=1e6+1;
int n,m,tot,sum,pos;
int first[N],next[N<<1],en[N<<1];
int a[N],p[M];
long long h[M],b[N],c[N],g[17][N],ans[N];
inline int read()
{
int X=0,w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
inline void insert(int x,int y)
{
next[++tot]=first[x];
first[x]=tot;
en[tot]=y;
}
inline int hash(int x)
{
int y=x%M;
while(h[y] && h[y]^x) y=(y+1)%M;
return y;
}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
{
int x=read(),y=read();
insert(x,y);
insert(y,x);
}
for(int i=1;i<=n;i++) a[i]=i;
for(int j=1,k=1;j<=n*16;j++,k=k+1>n?1:k+1)
{
for(int i=first[k];i;i=next[i])
{
c[a[en[i]]]+=k-b[en[i]];
b[en[i]]=k;
a[en[i]]=a[k];
}
//for(int i=1;i<=n;i++) c[a[i]]++;
if(k==n)
{
for(int i=1;i<=n;i++) c[a[i]]+=k-b[i];
memset(b,0,sizeof(b));
long long key=0;
for(int i=1;i<=n;i++) key=(key*10+a[i])%mo;
int k=hash(key);
if(h[k])
{
pos=p[k];
break;
}else
{
h[k]=key,p[k]=++sum;
memcpy(g[sum],c,sizeof(g[sum]));
}
}
}
for(int i=1;i<=n;i++) b[i]=c[i]-g[pos][i];
tot=0;
double num=1.0/(1.0*n*(sum-pos+1)),ext=1e-6;
for(int i=1;i<=n;i++)
if(b[i]>=ext) ans[++tot]=b[i];
sort(ans+1,ans+1+tot);
for(int i=tot;i;i--) printf("%.6lf\n",1.0*ans[i]*num);
return 0;
}