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 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;
}