CF1572D

将每个人视为一个节点,可以一组的人连边

对于连边的两个人,他们的二进制中1的个数差为1,奇偶不同

所以这个图构成一个二分图,转化为二分图最大权匹配

而注意到,每选择一条边,有最多 \(2n-2\)条边不能选了,因此我们只需要保留边权最大的 \(2nk\) 条边即可

然后跑一个费用流即可

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
int count(int st){
	int sum=0;
	while(st)sum+=st&1,st>>=1;
	return sum;
}
int n,k,a[(1<<20)+5];
struct edge{
	int v,w,cst,nx;
}e[50005];
int cnt=1,hd[(1<<20)+15];
void add(int u,int v,int w,int c){
	e[++cnt]=edge{v,w,c,hd[u]};
	hd[u]=cnt;
}
void add_edge(int u,int v,int w,int c){
//	printf("%d %d %d %d\n",u,v,w,c);
	add(u,v,w,c);
	add(v,u,0,-c);
}
int s,t,tot;
priority_queue<pair<int,pii>,vector<pair<int,pii>>,greater<pair<int,pii>>> pq;
vector<int> nec;
int vst[(1<<20)+15];
int flow[(1<<20)+15],dist[(1<<20)+15],pre[(1<<20)+15];
bool vis[(1<<20)+15];
bool spfa(){
	for(int i:nec)dist[i]=0x3f3f3f3f,flow[i]=0,pre[i]=0;
	queue<int> q;q.push(s);vis[s]=1;
	flow[s]=0x3f3f3f3f;dist[s]=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		vis[u]=0;
		for(int i=hd[u];i;i=e[i].nx){
			int v=e[i].v;
			if(dist[v]<=dist[u]+e[i].cst || e[i].w<=0)continue;
			dist[v]=dist[u]+e[i].cst;
			flow[v]=min(flow[u],e[i].w);
			pre[v]=i;
			if(!vis[v])q.push(v),vis[v]=1;
		}
	}
	return dist[t]!=0x3f3f3f3f;
}
int solve(){
	int res=0;
	while(spfa()){
		res+=flow[t]*dist[t];
		int u=t;
		while(u!=s){
			int id=pre[u];
			e[id].w-=flow[t];e[id^1].w+=flow[t];
			u=e[id^1].v;
		}
	}
	return res;
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=0;i<(1<<n);i++)scanf("%d",&a[i]);
	for(int i=0;i<(1<<n);i++)
		for(int j=0;j<n;j++)
			if(i<(i^(1<<j))){
				pq.push(mp(a[i]+a[i^(1<<j)],mp(i,i^(1<<j))));
				tot++;
				if(tot>2*n*k)pq.pop(),tot--;
			}
	while(!pq.empty()){
		int u=pq.top().se.fi,v=pq.top().se.se,w=pq.top().fi;pq.pop();
		if(count(u)%2==1)swap(u,v);
		add_edge(u,v,1,-w);
		vst[u]=1;vst[v]=2;
	}
	s=(1<<20);t=(1<<20)+1;
	for(int i=0;i<(1<<n);i++)
		if(vst[i]==1)add_edge(t+1,i,1,0),nec.pb(i);
		else if(vst[i]==2)add_edge(i,t+2,1,0),nec.pb(i);
	add_edge(s,t+1,k,0);
	add_edge(t+2,t,k,0);
	nec.pb(s);nec.pb(t);
	nec.pb(t+1);nec.pb(t+2);
	printf("%d\n",-solve());
	return 0;
}
posted @ 2025-11-18 19:02  Kent530  阅读(4)  评论(0)    收藏  举报