zoukankan      html  css  js  c++  java
  • 6478. 【GDOI2020模拟02.19】C

    题目

    给你一棵大小为$n$的树,还有$m$条路径。 每条路径都可以染成红色或蓝色,各有一定的贡献。 对于每一条边,每条经过它的红色路径会带来一些贡献,每条经过它的蓝色路径也会带来一些贡献。 而且经过这条边的红色路径和蓝色路径的条数都有上限。 问满足所有限制条件之后的最小贡献是多少。


    思考历程

    打了前两档暴力,并且由于没有输出-1,所以第一档暴力还挂了。 第三档暴力想出了暴力dp的做法,但没有时间打。


    正解

    显然是个线性规划嘛,只是想不想得到的问题。

    先说pty的做法: 先假设所有的路径都选红色,接下来要将一些路径改选成蓝色。 那么每条边上,经过它的路径中改选成蓝色的路径条数在一定区间内(记为$[L_u,R_u]$) 先计算出每条路径改选之后贡献。 设$x_i$为第$i$条路径选或不选的状态。 考虑对一条边$(u,fa)(的限制:)sum_{i为经过(u,fa)的路径}in [L_u,R_u]$ 可以转化成这样的限制$sum+a_u=R_u$,(sum{x_i}-b_u=L_u) 两式相减,得$a_u+b_u=R_u-L_u$,相当于$a_uleq R_u-L_u$ 于是这就成了个下界为$0$的差分约束模型:$x_ileq 1$并且$a_uleq R_u-L_u$,要满足$sum x_i+a_u=R_u$,求最小贡献。 考虑对于一棵树上,每个点都有个方程,将这个方程跟所有儿子的方程作差,会得到什么。 (sum_{i为u出发的路径}x_i-sum_{i为u结束的路径}x_i+a_u-sum a_{son}=R_u-sum R_{son}) 考虑费用流。假如式子左边是$F_出-F_入$ 对于每个路径$(x,y)$,$y$向$x$连边$(1,V)$($V$表示贡献)。$y$的流出加一,$x$的流入加一。 对于每条边$(u,fa)$,$u$向$fa$连边$(R_u-L_u,0)$。分析类似。 这样连边之后,$F_出-F_入$不一定会为$0$。为了保证流量平衡,对于$R_u-sum R_$为v正数的点$u$,连$(S,u)$补够流入;反之同理。 接下来跑最小费用最大流即可。有解的条件是:和源点汇点连的边都满流。 (我终于发现了原来zkw费用流跑带负权的边不靠谱,于是打了dinic。)

    然后说题解的做法: 同样也要进行一开始的处理。 对于每条边$(u,fa)(,连一条上下界为)[L_u,R_u]$的费用为$0$的边。 对于每条路径$(x,y)$,从$S$向$y$连上下界为$[1,1]$的费用为$0$的边。 从$x$向$T$连一条上下界为$[1,1]$的费用为$0$的边。 从$y$向$x$连一条上下界为$[0,1]$的费用为$V$的边。($V$为红改蓝的贡献) 跑上下界费用流。 这样给$y$的$f_出-f_入$减一,给$x$的$f_出-f_入$加一。 要使流量平衡,要么走$(y,x)$,意味着红变蓝;要么走树上的路径,保留原样。


    代码

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <climits>
    #include <cassert>
    #define N 160
    int n,m;
    struct EDGE{
    	int to;
    	EDGE *las;
    } e[N];
    int ne;
    EDGE *last[N];
    int fa[N],A[N],B[N],eA[N],eB[N];
    int sumA[N],sumB[N];
    int bz[N],tmp[N];
    struct Path{
    	int u,v,pA,pB;
    } p[N];
    
    struct EDGE2{
    	int to,c,w;
    	EDGE2 *las;
    } e2[N*3*2];
    int ne2;
    EDGE2 *last2[N];
    int S,T;
    #define rev(ei) (e2+(int((ei)-e2)^1))
    inline void link(int u,int v,int c,int w){
    	e2[ne2]={v,c,w,last2[u]};
    	last2[u]=e2+ne2++;
    	e2[ne2]={u,0,-w,last2[v]};
    	last2[v]=e2+ne2++;
    }
    bool vis[N];
    int dis[N];
    EDGE2 *cur[N];
    int maxflow,mincost;
    int dfs(int x,int s){
    	if (x==T){
    		maxflow+=s;
    		mincost+=dis[T]*s;
    		return s;
    	}
    	vis[x]=1;
    	int have=s;
    	for (EDGE2 *ei=last2[x];ei;ei=ei->las)
    		if (ei->c && !vis[ei->to] && dis[x]+ei->w==dis[ei->to]){
    			int t=dfs(ei->to,min(have,ei->c));
    			ei->c-=t,rev(ei)->c+=t,have-=t;
    			if (!have)
    				return s;
    		}
    	return s-have;
    }
    int cop[N];
    int q[N*N];
    bool inq[N];
    inline bool change(){
    	memcpy(cop,dis,sizeof dis);
    	memset(dis,127,sizeof dis);
    	dis[S]=0;
    	inq[S]=1;
    	int head=0,tail=0;
    	q[0]=S;
    	while (head<=tail){
    		int x=q[head++];
    		for (EDGE2 *ei=last2[x];ei;ei=ei->las)
    			if (ei->c && dis[x]+ei->w<dis[ei->to]){
    				dis[ei->to]=dis[x]+ei->w;
    				if (!inq[ei->to]){
    					q[++tail]=ei->to;
    					inq[ei->to]=1;
    				}
    			}
    		inq[x]=0;
    	}
    	for (int i=1;i<=T;++i)
    		if (dis[i]!=cop[i])
    			return 1;
    	return 0;
    }
    inline void flow(){
    	mincost=maxflow=0;
    	while (change())
    		do
    			memset(vis,0,sizeof vis);
    		while (dfs(S,INT_MAX));
    }
    void init(int x){
    	for (EDGE *ei=last[x];ei;ei=ei->las){
    		sumA[ei->to]=sumA[x]+eA[ei->to];
    		sumB[ei->to]=sumB[x]+eB[ei->to];
    		init(ei->to);
    	}
    }
    void init2(int x){
    	int s=B[x];
    	for (EDGE *ei=last[x];ei;ei=ei->las){
    		init2(ei->to);
    		A[ei->to]=bz[ei->to]-A[ei->to];
    		link(ei->to,x,B[ei->to]-A[ei->to],0);
    		bz[x]+=bz[ei->to];
    		s-=B[ei->to];
    	}
    	if (s>0)
    		link(S,x,s,0);
    	else if (s<0)
    		link(x,T,-s,0);
    }
    int main(){
    	freopen("C.in","r",stdin);
    	freopen("C.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	bool _2=1;
    	for (int i=2;i<=n;++i){
    		scanf("%d%d%d%d%d",&fa[i],&A[i],&B[i],&eA[i],&eB[i]);
    		A[i]=min(A[i],m);
    		B[i]=min(B[i],m);
    		e[ne]={i,last[fa[i]]};
    		last[fa[i]]=e+ne++;
    	}
    	S=n+1,T=n+2;
    	init(1);
    	for (int i=2;i<=n;++i)
    		eB[i]-=eA[i];
    	int sum=0;
    	for (int i=1;i<=m;++i){
    		scanf("%d%d%d%d",&p[i].u,&p[i].v,&p[i].pA,&p[i].pB);
    		int u=p[i].u,v=p[i].v;
    		bz[v]++,bz[u]--;
    		p[i].pA+=sumA[v]-sumA[u];
    		p[i].pB+=sumB[v]-sumB[u];
    		p[i].pB-=p[i].pA;
    		sum+=p[i].pA;
    		link(p[i].v,p[i].u,1,p[i].pB);
    	}
    	init2(1);
    	for (int i=1;i<=n;++i)
    		if (A[i]>B[i]){
    			printf("-1
    ");
    			return 0;
    		}
    	flow();
    	int tmp=0;
    	for (EDGE2 *ei=last2[S];ei;ei=ei->las)
    		if (ei->c){
    			printf("-1
    ");
    			return 0;
    		}
    	for (EDGE2 *ei=last2[T];ei;ei=ei->las)
    		if (rev(ei)->c){
    			printf("-1
    ");
    			return 0;
    		}
    	printf("%d
    ",mincost+sum);
    	return 0;
    }
    

    总结

    线性规划真是个大坑……

  • 相关阅读:
    低代码能做什么?这家服务商用钉钉宜搭打造了智慧医院管理应用
    【深度】阿里巴巴万级规模 K8s 集群全局高可用体系之美
    如何做规划?分享2种思维和4个方法
    配置审计(Config)配合开启OSS防盗链功能
    被解救的代码
    物联网海量时序数据存储有哪些挑战?
    Serverless:这真的是未来吗?(一)
    数据库学习之MySQL进阶
    网页三剑客之CSS
    网页三剑客之HTML
  • 原文地址:https://www.cnblogs.com/jz-597/p/12343379.html
Copyright © 2011-2022 走看看