ICPC WF Moscow Invitational Contest(Kingdom of Islands-枚举子集)

1 1 1 n ≤ 1 e 5 n\le 1e5 n1e5个点的图,每个点有权值 s i s_i si。若 2 2 2个点 s i s_i si不同,则连边,否则不连边。
k ≤ 20 k \le 20 k20个特例:原来连边变成不连,或者不连变成连。
求最大完全子图

如果没有特例点,每个 s i s_i si取一个点即可。
考虑有特例的情况:
两个点从连边变成不连:枚举哪个点不在子图中
两个点从不连边变成连:枚举是否两个点都在子图中
剩下贪心

#include<bits/stdc++.h> 
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define ForkD(i,k,n) for(int i=n;i>=k;i--)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i>0;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define Forpiter(x) for(int &p=iter[x];p;p=next[p])  
#define Lson (o<<1)
#define Rson ((o<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,0x3f,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define MEMx(a,b) memset(a,b,sizeof(a));
#define INF (0x3f3f3f3f)
#define F (1000000007)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define vi vector<int> 
#define pi pair<int,int>
#define SI(a) ((a).size())
#define Pr(kcase,ans) printf("Case #%d: %lld\n",kcase,ans);
#define PRi(a,n) For(i,n-1) cout<<a[i]<<' '; cout<<a[n]<<endl;
#define PRi2D(a,n,m) For(i,n) { \
						For(j,m-1) cout<<a[i][j]<<' ';\
						cout<<a[i][m]<<endl; \
						} 
#pragma comment(linker, "/STACK:102400000,102400000")
#define ALL(x) (x).begin(),(x).end()
#define gmax(a,b) a=max(a,b);
#define gmin(a,b) a=min(a,b);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
ll mul(ll a,ll b){return (a*b)%F;}
ll add(ll a,ll b){return (a+b)%F;}
ll sub(ll a,ll b){return ((a-b)%F+F)%F;}
void upd(ll &a,ll b){a=(a%F+b%F)%F;}
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while(!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
	while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
	return x*f;
} 
#define MAXN (100000+10)
 
#define MAXM (21)
int P,n;
int s[MAXN];
int c[MAXN];
int k;
int u[MAXM],v[MAXM];
map<pair<int,int>,int > h;
bool check(vi v,vi ban) {
	int sz=SI(v);
	Rep(i,sz) Fork(j,i+1,sz-1) {
		int x=v[i],y=v[j];
//		cout<<(s[x]!=s[y])<<' '<<(h.count(mp(x,y))) <<endl;
		if((s[x]!=s[y]) ==(h.count(mp(x,y))>0) ) return 0;
	}
	return 1;
}
int t_ck[MAXN]={},clo=0;
bool is_ck[MAXN]={};
bool is_ban[MAXN]={};
 
int main()
{
//	freopen("k.in","r",stdin);
//	freopen(".out","w",stdout);
//  cin.tie(0)->sync_with_stdio(0); 
	P=read(),n=read();
	For(i,n) s[i]=read(),c[s[i]]++;
	k=read();
	Rep(i,k) u[i]=read(),v[i]=read(),h[mp(u[i],v[i])]=h[mp(v[i],u[i])]=1;
	int S=1<<k;
	int ans=0;
	vi ex1,ba1;
	Rep(st,S) {
		vi exist,ban;
		++clo;
		Rep(i,k) {
			bool fl=(st>>i)&1;
			int u=::u[i],v=::v[i]; 
			if(s[u]==s[v]) {
				if(fl) {
					exist.pb(u);
					exist.pb(v);				
					t_ck[u]=t_ck[v]=clo;
				}
			}else {
				if(fl) {
					ban.pb(u);
				}else ban.pb(v);
			}
		}
		sort(ALL(exist));
		exist.erase(unique(ALL(exist)),exist.end() );
		sort(ALL(ban));
		ban.erase(unique(ALL(ban)),ban.end() );
		
		bool flag=0;
		for(auto p:ban) if(t_ck[p]==clo) {flag=1;break;}
		if(flag) continue;
		else if(!check(exist,ban)) continue;
		else {
			int z=0;
			for(auto x:exist) {
				int p=s[x];
				if(c[p]>0) ++z;
				c[p]-=1e7;
			}
			for(auto x:ban) {
				int p=s[x];
				--c[p];
				z+=c[p]==0;
			}
			z=SI(exist)+P-z;
			if(ans<z) {
				ans=z;
				ex1.assign(exist.begin(),exist.end());
				ba1.assign(ban.begin(),ban.end());
			}
//			ans=max(ans,z);
			for(auto x:ban) {
				int p=s[x];
				++c[p];
			}
			for(auto x:exist) {
				int p=s[x];
				c[p]+=1e7;
			}
 
		}
	}
	cout<<ans<<endl;
	for(auto x:ex1){
		is_ck[s[x]]=1;
	}
	for(auto x:ba1) {
		is_ban[x]=1;
	}
	For(i,n) if(!is_ban[i] && is_ck[s[i]]==0) {
		is_ck[s[i]]=1;
		ex1.pb(i);
	}
	int sz=SI(ex1);
	Rep(i,SI(ex1)) {
		cout<<ex1[i];
		putchar(i<sz-1?' ':'\n');
	}
//	for(auto x:ex1) cout<<x<<' ';
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值