题意:
给你一个图,问你有多少个方案把他分成连个新的图。使得一个图是一个团,另外一个是独立集
一些闲话:
以前做过一次这个题..当时听说爆搜可以过,就无脑莽过去了..
也没有思考为什么爆搜能过,或者有没有非爆搜的方法..
其实这题是有非爆搜,并且是线性的做法
题解:
先考虑这么一个事实:加入我们得到了一个合法解,那么剩下的合法解肯定可以通过下面的调整得到:
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同阶的点
然后就没什么了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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 ");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 ",ans,n); ans=ans+(cnt1+1)*(cnt2+1)-1; if (cnt==1&&cnt1==1) ans--; // printf("%d %d %d %d ",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! "); ans=ans+tot-now; } printf("%d ",ans); } return 0; }