LOJ #3400 - 「2020-2021 集训队作业」Storm(随机化+费用流)

首先先观察到一个性质:我们选出的边集一定不会成环。因为如果成环去掉任意一个环边一定更优,也就是说我们选出的边集的导出子图一定是一个森林。

由于是森林,必然可以对其进行二分图染色,于是很 nb 的一步来了,考虑随机对 \(n\) 个点黑白染色,然后网络流建图:

  • 新建超级源点 \(S'\) 和源点 \(S\),连边 \(S'\to S\),容量 \(k\) 费用 \(0\)
  • 对于黑点 \(x\),连边 \(S\to x\),容量 \(\infty\) 费用 \(-v_i\),白点则连向 \(T\)
  • 对于原图中的一条边 \((u,v,w)\),如果 \(u,v\) 颜色不同,那么假设 \(u\) 是黑点,连边 \(u\to v\),容量 \(1\) 费用 \(w\)
  • 连边 \(S\to T\),容量 \(\infty\) 费用 \(0\),表示选出的边集不一定要恰好满 \(k\) 条。

跑最小费用最大流,用最小费用的相反数更新答案。显然每次染色跑出的方案都是合法的,而每次我们有 \(2^{-k}\) 的概率把最优解对应的方案包含在其中。于是随 \(10·2^k\) 次即可。

感觉难点就一步?看完题解之后你可能会长叹一声:确实!但赛时还是被薄纱了,毕竟看到 \(k\) 很小的限制第一反应是神秘状压的人应该不占少数吧(

const int MAXN=5e5+3;
const int MAXE=5e6;
const int INF=2e9+5;
int n,m,k,a[MAXN+5],col[MAXN+5];mt19937 rng(time(0));
struct edge{int u,v,w;}e[MAXN+5];
namespace MCMF{
	int S,T,SS,hd[MAXN+5],to[MAXE+5],nxt[MAXE+5],cap[MAXE+5],cst[MAXE+5],ec=1;
	void clear(){for(int i=1;i<=T;i++)hd[i]=0;ec=1;}
	void adde(int u,int v,int f,int c){
		to[++ec]=v;cap[ec]=f;cst[ec]=c;nxt[ec]=hd[u];hd[u]=ec;
		to[++ec]=u;cap[ec]=0;cst[ec]=-c;nxt[ec]=hd[v];hd[v]=ec;
	}
	int dis[MAXN+5],pre[MAXN+5],lste[MAXN+5];bool inq[MAXN+5];
	bool getdis(){
		for(int i=1;i<=T;i++)dis[i]=INF,inq[i]=pre[i]=lste[i]=0;
		queue<int>q;q.push(S);dis[S]=0;
		while(!q.empty()){
			int x=q.front();q.pop();inq[x]=0;
			for(int e=hd[x];e;e=nxt[e]){
				int y=to[e],z=cap[e],w=cst[e];
				if(z&&dis[y]>dis[x]+w){
					dis[y]=dis[x]+w;pre[y]=x;lste[y]=e;
					if(!inq[y])q.push(y),inq[y]=1;
				}
			}
		}return dis[T]!=INF;
	}
	pii mcmf(){
		int mxfl=0,mncst=0;
		while(getdis()){
			int fl=INF;
			for(int i=T;i!=S;i=pre[i])chkmin(fl,cap[lste[i]]);
			mxfl+=fl;mncst+=fl*dis[T];
			for(int i=T;i!=S;i=pre[i])cap[lste[i]]-=fl,cap[lste[i]^1]+=fl;
		}return mp(mxfl,mncst);
	}
}using namespace MCMF;
int calc(){
	clear();
	for(int i=1;i<=n;i++)col[i]=rng()%2;
	adde(S,SS,k,0);adde(SS,T,INF,0);
	for(int i=1;i<=n;i++){
		if(!col[i])adde(SS,i,INF,0),adde(SS,i,1,-a[i]);
		else adde(i,T,INF,0),adde(i,T,1,-a[i]);
	}
	for(int i=1;i<=m;i++)if(col[e[i].u]^col[e[i].v]){
		if(!col[e[i].u])adde(e[i].u,e[i].v,1,e[i].w);
		else adde(e[i].v,e[i].u,1,e[i].w);
	}return -mcmf().se;
}
void solve(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=m;i++)scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
	SS=n+1;S=n+2;T=n+3;int lim=10<<k,res=0;while(lim--)chkmax(res,calc());
	printf("%d\n",res);
}
int main(){int qu;scanf("%d",&qu);while(qu--)solve();return 0;}
posted @ 2023-05-01 15:42  tzc_wk  阅读(49)  评论(0编辑  收藏  举报