JZOJ 5490. 【清華集訓2017模擬11.28】圖染色
阿新 • • 發佈:2019-02-02
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;
}