bzoj 3579: 破冰派对

题意:

给你一个图,问你有多少个方案把他分成连个新的图。使得一个图是一个团,另外一个是独立集

一些闲话:

以前做过一次这个题..当时听说爆搜可以过,就无脑莽过去了..

也没有思考为什么爆搜能过,或者有没有非爆搜的方法..

其实这题是有非爆搜,并且是线性的做法

题解:

先考虑这么一个事实:加入我们得到了一个合法解,那么剩下的合法解肯定可以通过下面的调整得到:

1.从团里面丢一个点到独立集里面

2.从独立集里面丢一个点到团里面

3.独立集和团交换一个点

显然,所有操作都不可能设计独立集/团里面的两个点,这样就使问题好办了许多

考虑怎么得到一个合法解

一般来说,我们可以贪心来做,按度数排一个序,然后从大到小看,能放到团里面就放到团里面,否则丢到独立集里面

最后看一下独立集是否合法..

尝试证明这个贪心的正确性,我们只需要证明只要有解,这样一定可以找出来即可

先假设i,j,按度数排序后i在j前面,如果存在一个方案是把i放到独立集里面,而把j放到团里面,不难得到i和j是等价的,因为j要和所有i连出的边连边

因此这两个点谁放哪里都一样

于是我们就得到了一个合法解

定义可以自由转化阵营的是自由点,否则是非自由点

两个都是自由点的方案很好算,两个都是非自由点的可以发现是没有的

那么各有一般怎么算..其实也是暴力就好了,看看唯一影响这个点非自由的是不是自由点就好了

这个部分可以写成O(m)

 

至于排序,可以用桶排来解决,因此总复杂度就是O(n+m)

但是因为这个题数据很不好造..因此爆搜什么的很难卡..n,m如果同阶,显然团的个数也会很少,因此,就算暴力一点写成O(n^2+m)也应该可以过n,m同阶的点

然后就没什么了

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
typedef long long LL;
const int N=1005;
int T;
int n,m;
struct qq
{
    int x,y,last;
}e[N*N*2];int num,last[N];
void init (int x,int y)
{
    e[++num].x=x;e[num].y=y;
    e[num].last=last[x];
    last[x]=num;
}
int du[N];
int id[N];
bool cmp (int x,int y)  {return du[x]>du[y];}
bool in[N];
int o[N];
int read ()
{
    char ch=getchar();int x=0;
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return x;
}
int main()
{
    T=read();
    while (T--)
    {
        num=0;memset(last,-1,sizeof(last));
        //scanf("%d%d",&n,&m);
        n=read();m=read();
        for (int u=1;u<=n;u++) du[u]=0;
        for (int u=1;u<=m;u++)
        {
            int x,y;
            //scanf("%d%d",&x,&y);
            x=read();y=read();
            init(x,y);init(y,x);
            du[x]++;du[y]++;
        }
        for (int u=1;u<=n;u++) id[u]=u,in[u]=false;
        sort(id+1,id+1+n,cmp);
        int cnt=0;//有多少个
        for (int u=1;u<=n;u++)
        {
            int x=id[u];int sum=0;
            for (int i=last[x];i!=-1;i=e[i].last) sum=sum+in[e[i].y];
            if (sum==cnt)   {in[x]=true;cnt++;}
        }
        bool tf=true;
        for (int u=1;u<=num;u++) if (in[e[u].x]==false&&in[e[u].y]==false) {tf=false;break;}
        if (tf==false) {printf("0\n");continue;}
        int cnt1=0,cnt2=0;
        for (int u=1;u<=n;u++)
        {
            o[u]=0;
            for (int i=last[u];i!=-1;i=e[i].last)   o[u]=o[u]+(in[u]!=in[e[i].y]);
            if (in[u]==true&&o[u]==0) cnt1++;
            if (in[u]==false&&o[u]==cnt) cnt2++;
        }
        int ans=(cnt!=n);//printf("%d %d\n",ans,n);
        ans=ans+(cnt1+1)*(cnt2+1)-1;
         
        if (cnt==1&&cnt1==1) ans--;
    //  printf("%d %d %d %d\n",ans,cnt1,cnt2,cnt);
        int tot=0;
        for (int u=1;u<=n;u++)  if (in[u]&&du[u]==cnt-1) tot++;
        for (int u=1;u<=n;u++) if (in[u]==false&&du[u]==cnt-1)
        {
            int now=0;
            for (int i=last[u];i!=-1;i=e[i].last)
            {
                int y=e[i].y;
                if (in[y]&&du[y]==cnt-1) now++;
            }
        //  if (now>tot) printf("FUCK!\n");
            ans=ans+tot-now;
        }
        printf("%d\n",ans);
    }
    return 0;
}
View Code

 

posted @ 2019-06-24 15:24  Als123  阅读(195)  评论(0编辑  收藏  举报