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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值