agc045_e Fragile Balls
agc045_e Fragile Balls
https://atcoder.jp/contests/agc045/tasks/agc045_e
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;
}