zoukankan      html  css  js  c++  java
  • [HZNOI #koishi] Magic

    [HZNOI #514] Magic

    题意

    给定一个 (n) 个点 (m) 条边的有向图, 每个点有两个权值 (a_i)(b_i), 可以以 (b_i) 的花费把第 (i) 个点的 (a_i) 变成 (0). 最后每个点 (i) 产生的花费为所有从 (i) 出发能通过一条有向边直接到达的点 (j)(a_j)(max). 最小化这个过程中的总花费.

    (nle 1000,mle50000)

    题解

    一点都不套路的最小割.

    果然我是不会网络流的.

    对于每个点, 如果将它的邻接点按照 (a_j) 降序排序的话, 不难发现必然要干掉一个前缀的所有 (a_j) 才能让这个点在最后统计的时候产生的花费变小. 但是多次干掉同一个点不能重复计算花费.

    那么我们一点都不自然地想到最小割. 先把所有点拆成两个, 一个负责计算最终统计时的花费 (A类点), 一个负责计算被干掉的时候产生的花费 (B类点). 被干掉的时候产生的花费直接连一条流量为 (b_i) 的边到 (t) 就可以了. 最终统计时的花费先从 (s) 连一条 (infty) 边到当前点, 然后按照 (a_j) 降序拉出一条链来, 链上的每个点代表一条边, 权值为这条边到达的点的 (a_j). 然后再从链上的每个点连一条 (infty) 边到 (j) 对应的点. 这样的话如果 (sverb|-|t) 被割断, 那么对于每一个 A 类点, 后面必然是割掉了某个 (a_j), 同时所有大于被割断的 (a_j) 的边邻接的点必然都已经被割掉了 (b_i).

    建图Dinic就可以了.

    这个拉链然后最小割的套路依然没有学会...果然我还是太菜了QAQ...

    什么你问我 (n+m) 个点Dinic怎么跑过去的? 我怎么知道?Dinic的运行速度大概都是靠信仰吧...

    恋恋世界第一!

    参考代码

    #include <bits/stdc++.h>
    
    const int MAXV=1e5+10;
    const int MAXE=5e6+10;
    const int INF=0x7FFFFFFF;
    
    struct Edge{
    	int from;
    	int to;
    	int flow;
    	Edge* rev;
    	Edge* next;
    };
    Edge E[MAXE];
    Edge* head[MAXV];
    Edge* cur[MAXV];
    Edge* top=E;
    
    int v;
    int e;
    int a[1010];
    int b[1010];
    int depth[MAXV];
    std::vector<int> link[1010];
    
    bool BFS(int,int);
    int Dinic(int,int);
    int DFS(int,int,int);
    void Insert(int,int,int);
    
    int main(){
    	freopen("magic.in","r",stdin);
    	freopen("magic.out","w",stdout);
    	scanf("%d%d",&v,&e);
    	for(int i=1;i<=v;i++)
    		scanf("%d",a+i);
    	for(int i=1;i<=v;i++)
    		scanf("%d",b+i);
    	for(int i=0;i<e;i++){
    		int a,b;
    		scanf("%d%d",&a,&b);
    		link[a].push_back(b);
    	}
    	for(int i=1;i<=v;i++)
    		std::sort(link[i].begin(),link[i].end(),[](int a,int b){return ::a[a]>::a[b];});
    	int s=0,t=1,cnt=v*2+1;
    	for(int i=1;i<=v;i++){
    		Insert(s,i+1,INF);
    		Insert(i+v+1,t,b[i]);
    		int last=i+1;
    		for(size_t j=0;j<link[i].size();j++){
    			++cnt;
    			Insert(cnt,v+link[i][j]+1,INF);
    			Insert(last,cnt,a[link[i][j]]);
    			last=cnt;
    		}
    	}
    	printf("%d
    ",Dinic(s,t));
    	return 0;
    }
    
    int Dinic(int s,int t){
    	int ans=0;
    	while(BFS(s,t))
    		ans+=DFS(s,INF,t);
    	return ans;
    }
    
    bool BFS(int s,int t){
    	memset(depth,0,sizeof(depth));
    	std::queue<int> q;
    	q.push(s);
    	depth[s]=1;
    	cur[s]=head[s];
    	while(!q.empty()){
    		s=q.front();
    		q.pop();
    		for(Edge* i=head[s];i!=NULL;i=i->next){
    			if(i->flow>0&&depth[i->to]==0){
    				depth[i->to]=depth[s]+1;
    				cur[i->to]=head[i->to];
    				if(i->to==t)
    					return true;
    				q.push(i->to);
    			}
    		}
    	}
    	return false;
    }
    
    int DFS(int s,int flow,int t){
    	if(s==t||flow<=0)
    		return flow;
    	int rest=flow;
    	for(Edge*& i=cur[s];i!=NULL;i=i->next){
    		if(i->flow>0&&depth[i->to]==depth[s]+1){
    			int tmp=DFS(i->to,std::min(rest,i->flow),t);
    			if(tmp<=0)
    				depth[i->to]=0;
    			rest-=tmp;
    			i->flow-=tmp;
    			i->rev->flow+=tmp;
    			if(rest<=0)
    				break;
    		}
    	}
    	return flow-rest;
    }
    
    inline void Insert(int from,int to,int flow){
    	top->from=from;
    	top->to=to;
    	top->flow=flow;
    	top->rev=top+1;
    	top->next=head[from];
    	head[from]=top++;
    	
    	top->from=to;
    	top->to=from;
    	top->flow=0;
    	top->rev=top-1;
    	top->next=head[to];
    	head[to]=top++;
    }
    
    

  • 相关阅读:
    PDA固定资产条码管理系统软件-解决固定资产实物清查的瓶颈问题,大大提高清查效率
    互联网+下PDA移动智能手持POS超市收银开单软件
    搭建免费代理池
    解析库beautifulsoup
    爬取汽车之家新闻
    请求库之requests库
    网络状态码301与302
    正向代理与反向代理
    垃圾回收机制详解
    HTTP协议详解
  • 原文地址:https://www.cnblogs.com/rvalue/p/10615423.html
Copyright © 2011-2022 走看看