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\not=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\n",an);
	}
	return 0;
}
posted @ 2020-06-30 10:56  LJZ_C  阅读(245)  评论(0编辑  收藏  举报