CF1572D Bridge Club

感觉这道题很吊。

首先我们我们先想到图的匹配,但是一般图最大权匹配你是不会想写的对吧,而且应该也过不了。

我们再来分析一波,发现每一个点只会和与其 \(1\) 个数奇偶性不同的点相连边,那么显然的,这其实是一张二分图,其中点的个数是 \(O(2^n)\) 级别的,边是 \(O(n2^n)\) 级别的。

直接跑必然是不能通过的,但是我们发现这里的 \(k\) 特别小,我们考虑利用这个优化。

能否尝试使用鸽巢原理,找到最大的若干个,使得不论我们怎么选,最优的选法始终被包含在这前若干个匹配中。我们考虑我们选择一条边的时候,最劣情况会使得 \(2(n-1)\) 条边不能选择,那么假设最优方案就是这么的劣,每次丢掉的都是最优的几个,那我们至少需要 \((k-1)(2n-1)+1\) 条边就可行了。


卡尼玛,正解是用原始对偶算的复杂度,我 SSP 根本过不了,别人都能过。

#include<bits/stdc++.h>
using namespace std;
const int N=20,K=2e2+5;
const int INF=1e9+7;
int n,m,k,a[1<<N];
struct Edge{int u,v,w;}bag[(1<<N)*N+1];
bool cmp(Edge a,Edge b){return a.w>b.w;}
int from,to,tot=0;map<int,int> mp;
struct edge{int nxt,to,flow,cost;}e[12*N*K];int fir[4*N*K],e_size=1;
void add(int u,int v,int w,int x){e[++e_size]=(edge){fir[u],v,w,x},fir[u]=e_size;}
bool count(int x){return x==0?0:((x&1)^count(x>>1));}
int cur[4*N*K],dis[4*N*K];bool vis[N];queue<int> q;
bool spfa(){
	for(int i=1;i<=tot;++i) cur[i]=fir[i],dis[i]=-INF;
	dis[from]=0,q.push(from),vis[from]=true;
	while(!q.empty()){
		int u=q.front();q.pop(),vis[u]=false;
		for(int i=fir[u];i;i=e[i].nxt){
			int v=e[i].to;if(!e[i].flow||dis[v]>=dis[u]+e[i].cost) continue;
			dis[v]=dis[u]+e[i].cost;if(!vis[v]) q.push(v),vis[v]=true;
		}
	}
	return dis[to]!=-INF;
}
int dfs(int u,int flow){
	if(u==to) return flow;
	int res=0;vis[u]=true;
	for(int i=cur[u];i&&flow;i=e[i].nxt){
		int v=e[i].to;cur[u]=i;
		if(vis[v]||!e[i].flow||dis[v]!=dis[u]+e[i].cost) continue;
		int tmp=dfs(v,min(flow,e[i].flow));
		e[i].flow-=tmp,e[i^1].flow+=tmp,flow-=tmp,res+=tmp;
	}
	return vis[u]=false,res;
}
int res=0;
int main(){
	cin>>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){
			int k=(i^(1<<j));if(k<i) continue;
			bag[++m]=(Edge){i,k,a[i]+a[k]};
		}
	}
	int cnt=min(m,(k-1)*(2*n-1)+1);
	nth_element(bag+1,bag+cnt,bag+1+m,cmp);
	from=++tot,to=++tot;
	for(int i=1;i<=cnt;++i){
		Edge tmp=bag[i];
		// printf("%d %d %d\n",tmp.u,tmp.v,tmp.w);
		if(!mp[tmp.u]){
			mp[tmp.u]=++tot;
			if(count(tmp.u)) add(from,mp[tmp.u],1,0),add(mp[tmp.u],from,0,0);
			else add(mp[tmp.u],to,1,0),add(to,mp[tmp.u],0,0);
		}
		if(!mp[tmp.v]){
			mp[tmp.v]=++tot;
			if(count(tmp.v)) add(from,mp[tmp.v],1,0),add(mp[tmp.v],from,0,0);
			else add(mp[tmp.v],to,1,0),add(to,mp[tmp.v],0,0);
		}
		if(count(tmp.u)) add(mp[tmp.u],mp[tmp.v],1,tmp.w),add(mp[tmp.v],mp[tmp.u],0,-tmp.w);
		else add(mp[tmp.v],mp[tmp.u],1,tmp.w),add(mp[tmp.u],mp[tmp.v],0,-tmp.w);
	}
	while(spfa()&&k){
		int tmp=dfs(from,INF);
		res+=dis[to]*min(tmp,k),k-=min(tmp,k);
	}
	return printf("%d\n",res),0;
}
/*
我人都傻了,机房初三巨佬直接秒了,我只能抓巴。
*/
posted @ 2021-12-20 16:39  Point_King  阅读(49)  评论(0)    收藏  举报