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;
}
posted @ 2021-03-17 12:09  WWW~~~  阅读(195)  评论(0编辑  收藏  举报