Gym 102471 K All Pair Maximum Flow 题解
Gym 102471 K All Pair Maximum Flow 题解
主要记录一种很方便的凸包找最小环的技巧。
问题描述(不是本题):
有一个\(n\)个点的凸包(外部连了一圈边,内部边只在交点处相交),每次删除凸包外围的一条边,同时维护外围的边集(注意可能会分裂成多个凸包)
做法:
其实每次删除一条边只需要找到两点之间的最短路就行了,然后将这条最短路上边的状态取反(是否在外层边集)
但是这会非常麻烦。
有一个很巧妙的做法:将边定向。
首先将最外层的边定向(假设标号按照顺时针):
然后随便删去一条边,蓝色边表示状态反转。
再定向:
这样还是可以保证顺时针排列。
其实我们只需要从删去的边反向出发,然后每次走到极角最小的就可以了。
本题代码:
/*
{
######################
# Author #
# Gary #
# 2021 #
######################
*/
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
const int MAXN=4e5+233;
const int MOD=998244353;
int n,m;
bool era[MAXN];
set<mp> g[MAXN];
int u[MAXN],v[MAXN];
LL w[MAXN];
set<pair<LL,int> > e;
void dfs(int now,int pre,int st,LL addi){
if(now==st) return ;
mp nex;
if(g[now].upper_bound(II(pre,INF))!=g[now].end()){
nex=*g[now].upper_bound(II(pre,INF));
}
else{
nex=*g[now].begin();
}
if(e.find(II(w[nex.SEC],nex.SEC))==e.end()){
w[nex.SEC]+=addi;
if(u[nex.SEC]!=now) swap(u[nex.SEC],v[nex.SEC]);
e.insert(II(w[nex.SEC],nex.SEC));
}
else{
g[u[nex.SEC]].erase(II(v[nex.SEC],nex.SEC));
g[v[nex.SEC]].erase(II(u[nex.SEC],nex.SEC));
e.erase(II(w[nex.SEC],nex.SEC));
w[nex.SEC]+=addi;
}
dfs(nex.FIR,now,st,addi);
}
void erase_edge(int idx){
era[idx]=1;
e.erase(II(w[idx],idx));
g[u[idx]].erase(II(v[idx],idx));
g[v[idx]].erase(II(u[idx],idx));
dfs(u[idx],v[idx],v[idx],w[idx]);
}
bool cmp(int A,int B){
return w[A]<w[B];
}
int siz[MAXN],fa[MAXN];
int root(int now){
return fa[now]=(fa[now]==now? now:root(fa[now]));
}
int main(){
scanf("%d%d",&n,&m);
rb(i,1,m){
scanf("%d%d%lld",&u[i],&v[i],&w[i]);
if(u[i]>v[i]) swap(u[i],v[i]);
if(v[i]==u[i]+1||(u[i]==1&&v[i]==n)) e.insert(II(w[i],i));
if(u[i]==1&&v[i]==n) swap(u[i],v[i]);
g[u[i]].insert(II(v[i],i));
g[v[i]].insert(II(u[i],i));
}
rb(i,1,m-n+1){
erase_edge(e.begin()->SEC);
}
vector<int> E;
rb(i,1,m){
if(!era[i]){
E.PB(i);
}
}
sort(ALL(E),cmp);
reverse(ALL(E));
rb(i,1,n) fa[i]=i,siz[i]=1;
int rest=0;
rb(i,1,m) w[i]%=MOD;
for(auto it:E){
u[it]=root(u[it]);
v[it]=root(v[it]);
// cout<<u[it]<<"_"<<v[it]<<' '<<w[it]<<endl;
(rest+=1ll*siz[u[it]]*siz[v[it]]%MOD*w[it]%MOD)%=MOD;
fa[u[it]]=v[it];
siz[v[it]]+=siz[u[it]];
}
cout<<rest<<endl;
return 0;
}