• agc045_e Fragile Balls


    agc045_e Fragile Balls

    https://atcoder.jp/contests/agc045/tasks/agc045_e

    Snipaste_2020-06-30_10-35-33.png

    Tutorial

    https://img.atcoder.jp/agc045/editorial.pdf

    将每个球看作一条从(a_i)连向(b_i)的有向边,称基图的联通块为"联通块",原图的强连通分量为"强连通分量"

    假如(C_i=1).即每个球只能移动一次的时候,发现有解的条件就是不存在长度大于等于(2)的简单环.

    首先必要性很显然,长度大于等于(2)的简单环是无法只靠自己移动的.

    考虑充分性,对于一个不是简单环的联通块,将它的强连通分量拓扑排序,对于所有可以成为拓扑序最小的强连通分量,其中一定存在某个度数大于等于(3)的点,以这个点为基础扩展,即可将这些强连通分量解决,而之后的强连通分量亦可在这基础上扩展.

    现在回到一般的情况,现在可以将图中的联通块分为(3)

    • 长度为(1)的自环
    • 长度大于等于(2)的简单环
    • 非简单环联通块

    花费指在 (P=sum [a_i ot=b_i]) 的基础上额外的花费.

    对于第(2)种联通块,需要从别的地方移动一个球过来,它可以贡献 (sum c_i-1) 次移动.花费(1)

    对于第(1)种联通块,如果从别的地方移动一个球过来,它可以贡献(c_i-1)次移动,花费(2).也可以不管.

    对于第(3)种联通块,其中不是自环的边贡献为(c_i-1),花费(0);自环的贡献是(c_i-1),花费(1)

    我们设第(2)种联通块的数量为(X),使用的第(1)种联通块数量为(Y),使用的第(3)种联通块中的自环的数量是(Z),则需要满足贡献和大于等于(X+Y),答案为(P+X+2Y+Z)

    考虑构造方式

    • (3)类联通块的某个点出发,按贡献的大小顺序移动至每个需要的联通块.如此扩展

    所以在(X+Y>0)时我们还需要保证(3)类联通块中的点做出过贡献.

    明白了这些条件后用双指针即可求出答案.

    Code

    https://atcoder.jp/contests/agc045/submissions/14060501

    #include <algorithm>
    #include <cstdio>
    #include <functional> 
    #include <iostream>
    #include <vector>
    #define debug(...) fprintf(stderr,__VA_ARGS__)
    using namespace std;
    inline char gc() {
    //	return getchar();
    	static char buf[100000],*l=buf,*r=buf;
    	return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
    }
    template<class T> void rd(T &x) {
    	x=0; int f=1,ch=gc();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    	while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=gc();}
    	x*=f;
    }
    template<class T> inline bool Cmin(T &x,T y) {return x>y?x=y,1:0;}
    const int inf=1e9;
    const int maxn=1e5+50;
    const int maxm=1e5+50;
    int n,m,a[maxm],b[maxm],c[maxm];
    int deg[maxn];
    bool good[maxn];
    vector<int> Y,Z; 
    namespace us {
    	int fa[maxn],siz[maxn];
    	void init(int n) {
    		for(int i=1;i<=n;++i) fa[i]=i,siz[i]=1;
    	}
    	int find(int a) {return a==fa[a]?a:fa[a]=find(fa[a]);}
    	inline bool merge(int a,int b) {
    		a=find(a),b=find(b);
    		if(a==b) return 0;
    		fa[a]=b,siz[b]+=siz[a];
    		return 1;
    	}
    }
    inline void addedge(int u,int v) {
    	us::merge(u,v);
    	++deg[u],++deg[v];
    }
    int main() {
    	rd(n),rd(m);
    	us::init(n);
    	for(int i=1;i<=m;++i) {
    		rd(a[i]),rd(b[i]),rd(c[i]),--c[i];
    		addedge(a[i],b[i]);
    	}
    	for(int i=1;i<=n;++i) if(deg[i]>2) good[us::find(i)]=1;
    	int X=0;
    	for(int i=1;i<=n;++i) if(us::find(i)==i) {
    		if(!good[i]&&us::siz[i]>=2) ++X;
    	}
    	int now=0; bool flag=0;
    	for(int i=1;i<=m;++i) if(c[i]) {
    		if(good[us::find(a[i])]) {
    			if(a[i]==b[i]) Z.push_back(c[i]);
    			else flag=1,now+=c[i];
    		}
    		else {
    			if(a[i]==b[i]) Y.push_back(c[i]-1);
    			else now+=c[i];
    		}
    	}
    	if(Y.size()) sort(Y.begin(),Y.end(),greater<int>());
    	if(Z.size()) sort(Z.begin(),Z.end(),greater<int>());
    	for(int i=0;i<Y.size();++i) now+=Y[i];
    	int an=inf;
    	for(int i=0,j=Y.size()-1;i<=Z.size();++i) {
    		while(j>=0&&now-Y[j]>=X) now-=Y[j--];
    		if((flag||i||X==0)&&now>=X) Cmin(an,i+2*(j+1));
    		if(i<Z.size()) now+=Z[i];
    	}
    	if(an==inf) puts("-1");
    	else {
    		for(int i=1;i<=m;++i) if(a[i]!=b[i]) ++an;
    		an+=X;
    		printf("%d
    ",an);
    	}
    	return 0;
    }
    
  • 相关阅读:
    2019 滴滴java面试笔试总结 (含面试题解析)
    2019 阿里java面试笔试总结 (含面试题解析)
    Mangos笔记
    Google 多源码管理工具 gclient
    构建系统介绍
    Ninja介绍
    小试 Ninja
    如何使用Ninja快速编译LLVM和Clang
    FMS4中的P2P功能
    Flex之HTML5视频播放解决方案
  • 原文地址:https://www.cnblogs.com/ljzalc1022/p/13212522.html
Copyright © 2020-2023  润新知