1. 程式人生 > >JZOJ 5490. 【清華集訓2017模擬11.28】圖染色

JZOJ 5490. 【清華集訓2017模擬11.28】圖染色

Description

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; }