20260526模拟赛

20260526模拟赛

通信网络

题面:

给定一张简单联通无向图和一个整数 \(K\),每条边有非负整数属性 \(c_i,d_i\),你需要为每条边选择一个非负整数 \(x_i\),设其边权为 \(c_i+x_i\) 的图上最小生成树边权和为 \(MST\),最大化 \(K\times MST-\sum d_ix_i\),或报告正无穷。\(n\leq 50,m\leq 200\)

题解:

\(T\) 为所有生成树的集合。对每棵生成树 \(t \in T\),记 \(f_{t,e} = [e\in t]\),令 \(v\) 表示最小生成树的权值,限制是 \(\forall t \in T, v \leq \sum_e (c_e + x_e) f_{t,e}\)

原问题写成线性规划形式是 \(\max kv - \sum d_e w_e\) 要求 \(\forall t \in T,v - \sum_e x_e f_{t,e} \leq \sum_e c_e f_{t,e}\) 变量 $ x_e \geq 0$。

对这个问题对偶之后变成:设每棵生成树变量是 \(y_t \geq 0\)\(\min\sum_t \left(\sum_e c_e f_{t,e}\right) y_t\) 限制 \(\sum_t y_t \geq k;\forall e \sum_t f_{t,e} y_t \leq d_e\)

\(w_e = \sum_t f_{t,e} y_t\),若 \(\sum_t y_t > k\),则一定存在方案 \(\sum_t y'_t=k,y'_t\leq y_t\),答案不会变劣。所以不妨令 \(\sum_t y_t = k\)。问题变为 \(\sum_e w_e = k(n-1), 0 \leq w_e \leq d_e\),存在 \(k\) 棵生成树对于每条边的累计覆盖次数是 \(w_e\),最小化 \(\sum_e c_e w_e\)

生成树覆盖这个略显抽象的条件有经典结论是它等价于 \(\sum_{e \in E(S)} w_e \leq k(|S| - 1) \quad (\forall \emptyset \neq S \subseteq V)\),其中 \(E(S)\) 表示点集 \(S\) 的导出子图中的边集。

将所有边按 \(c_e\) 从小到大排序。依次考虑每条边 \(i\),尽可能让它的 \(w_i\) 取到最大。设当前边 \(i\) 的两个端点为 \(u,v\),前面已经确定的边权记为 \(g_e\),后面的边暂时视作 \(0\)。那么 \(w_i\) 的最大可取值为:\(w_i = \min\left(d_i,\ \min_{u,v \in S} \left(k(|S| - 1) - \sum_{e \in E(S), e \neq i} g_e\right)\right)\)

设当前为 \((u,v)\),要求出 \(\min_{u,v \in S} \left(k(|S| - 1) - \sum_{e \in E(S), e \neq i} g_e\right)\),这可以最小割解决:

对于每条已确定的边 \(e=(a,b)\),加入边 \((S,a,g_e),(a,b,g_e)\)。对于每个顶点 \(x \neq u,v\),加入边 \((x,T,k)\)

最小割值为 \(\text{cut}=\sum g_e-\sum_{e\in E(S),e\not =i}g_e+k(|S|-2)\)。其中一个点被割到了源点集合中表示其在 \(S\) 中,我们连边时钦定 \(u,v\) 已经在 \(S\) 中。

最后若 \(\sum g_e=k(n-1)\) 不成立则答案为正无穷。

代码
    #include<bits/stdc++.h>
    #define ll long long
    #define fir first
    #define sec second
    using namespace std;
    
    inline int read(){
    	int s=0,k=1;
    	char c=getchar();
    	while(c>'9'||c<'0'){
    		if(c=='-') k=-1;
    		c=getchar();
    	}
    	while(c>='0'&&c<='9'){
    		s=(s<<3)+(s<<1)+(c^48);
    		c=getchar();
    	}
    	return s*k;
    }
    
    const int N=55,M=205;
    const ll inf=1e18+7;
    int n,m;ll K,w[M];
    struct edge{
    	int u,v;ll c,d;
    }e[M];
    
    namespace flow{
    	int head[N],E[N],dep[N],cnt,S,T;
    	struct edge{
    		int v,nxt;ll w;
    	}e[M*2+N<<1];
    	
    	void init(int n){
    		S=0;T=n+1;cnt=1;
    		for(int i=S;i<=T;i++) head[i]=0;
    	}
    	
    	void add(int u,int v,ll w){
    		e[++cnt]={v,head[u],w};
    		head[u]=cnt;
    		e[++cnt]={u,head[v],0};
    		head[v]=cnt;
    	}
    	
    	bool bfs(){
    		for(int i=S;i<=T;i++) dep[i]=0,E[i]=head[i];
    		queue<int>q; q.push(S);dep[S]=1;
    		while(!q.empty()){
    			int x=q.front();q.pop();
    			for(int i=head[x],v;i;i=e[i].nxt){
    				v=e[i].v;
    				if(e[i].w&&!dep[v]){
    					dep[v]=dep[x]+1;
    					q.push(v);
    				}
    			}
    		}
    		return dep[T]!=0;
    	}
    	
    	ll dfs(int x,ll W){
    		if(x==T) return W;
    		ll now=0;
    		for(int i=E[x],v;i;i=e[i].nxt){
    			E[x]=i;v=e[i].v;
    			if(e[i].w&&dep[v]==dep[x]+1){
    				ll tmp=dfs(v,min(e[i].w,W-now));
    				if(!tmp) dep[v]=-1;
    				else{
    					now+=tmp;
    					e[i].w-=tmp;
    					e[i^1].w+=tmp;
    					if(now==W) return now;
    				}
    			}
    		}
    		return now;
    	}
    	
    	ll dinic(){
    		ll ans=0,x=0;
    		while(bfs()) while(x=dfs(S,inf)) ans+=x;
    		return ans;
    	}
    }
    
    int main(){
    	freopen("network.in","r",stdin);
    	freopen("network.out","w",stdout);
    	n=read();m=read();K=read();
    	for(int i=1;i<=m;i++){
    		int u=read(),v=read(),c=read(),d=read();
    		e[i]={u,v,c,d};
    	}
    	sort(e+1,e+1+m,[&](edge x,edge y){
    		return x.c<y.c;
    	});
    	ll sum=0;
    	for(int i=1;i<=m;i++){
    		int u=e[i].u,v=e[i].v;
    		flow::init(n);
    		for(int j=1;j<=m;j++){
    			if(w[j]==0) continue;
    			flow::add(0,e[j].u,w[j]);
    			flow::add(e[j].u,e[j].v,w[j]);
    		}
    		for(int i=1;i<=n;i++)
    			if(i!=u&&i!=v) flow::add(i,n+1,K);
    		ll ans=flow::dinic();
    		w[i]=min(e[i].d,ans+K-sum);
    		sum+=w[i];
    	}
    	if(sum!=K*(n-1)){
    		puts("-1");
    		return 0;
    	}
    	ll ans=0;
    	for(int i=1;i<=m;i++) ans+=e[i].c*w[i];
    	printf("%lld",ans);
    	return 0;
    }
posted @ 2026-06-08 17:06  programmingysx  阅读(7)  评论(0)    收藏  举报
Title