【CodeChef】最大与最小 Maximum and Minimum(最小生成树,树分治,虚树,线段树分治)

22 篇文章 0 订阅
4 篇文章 0 订阅

背景

在这里,让世界感受树据结构!

题面

🔗
在这里插入图片描述
在这里插入图片描述

题解

如果存在一个环,很容易发现该环中的最大边一定没用,所以可以把最大边删掉。

于是得到结论:只需要保留这两个图的最小生成树。

然后我们要统计每个 G 1 G_1 G1 中的路径的贡献,可以用边分治或点分治。点分治更好打,就用点分治吧

我们得到分治中心后,遍历整棵连通子树,记录每个点到根的最大边权 m x i mx_i mxi,两个点的 f ( G 1 , i , j ) f(G_1,i,j) f(G1,i,j) 就等于 max ⁡ ( m x i , m x j ) \max(mx_i,mx_j) max(mxi,mxj) (当然,这里会多算入不合法的折返路径,但是可以容斥减掉)。

我们把这棵连通子树的点在 G 2 G_2 G2 上建出虚树,把虚树的边按边权从小到大排序,依次加入,每次加入会合并两棵树,我们用线段树合并可以求出经过这条边的所有 G 2 G_2 G2 路径的 f ( G 1 , i , j ) f(G_1,i,j) f(G1,i,j) 之和,再乘上这条边的权值,就是这些路径的贡献和。

最后梳理一下流程:建出两张图的最小生成树,对第一棵树进行点分治,点分时将连通子树的点在 G 2 G_2 G2 上建出虚树,在虚树的Kruskal重构树上进行线段树合并统计答案。

时间复杂度 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n)

CODE

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<random>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 200005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB long double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
//#define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

const int MOD = 998244353;
int n,m,s,o,k;
int hd1[MAXN],hd2[MAXN],nx[MAXN<<3],v[MAXN<<3],w[MAXN<<3],cne;
void ins(int *hd,int x,int y,int z) {
	nx[++cne] = hd[x]; v[cne] = y; w[cne] = z; hd[x] = cne;
}
int U[MAXN],V[MAXN],W[MAXN],b[MAXN];
bool cmp(int x,int y) {return W[x] < W[y];}
int sum;
struct it{
	int nm,ct;
	int ls,rs;
	it(){nm=ct=ls=rs=0;}
}tre[MAXN*32];
void upd(int a) {
	tre[a].nm = (tre[tre[a].ls].nm + tre[tre[a].rs].nm) % MOD;
	tre[a].ct = tre[tre[a].ls].ct + tre[tre[a].rs].ct;
}
int cnt;
int addtree(int a,int x,int al,int ar) {
	if(al > x || ar < x) return a;
	if(!a) tre[a = ++ cnt] = it();
	if(al == ar) return tre[a].nm = x,tre[a].ct = 1,a;
	int md = (al + ar) >> 1;
	tre[a].ls = addtree(tre[a].ls,x,al,md);
	tre[a].rs = addtree(tre[a].rs,x,md+1,ar);
	return upd(a),a;
}
int merg(int a,int b,int al,int ar) {
	if(!a || !b) return a+b;
	if(al == ar) {
		sum = (tre[a].nm*1ll*tre[b].ct + sum) % MOD;
		(tre[a].nm += tre[b].nm) %= MOD; tre[a].ct += tre[b].ct;
		return a;
	}
	int md = (al + ar) >> 1;
	sum = (tre[tre[a].rs].nm*1ll*tre[tre[b].ls].ct + tre[tre[b].rs].nm*1ll*tre[tre[a].ls].ct + sum) % MOD;
	tre[a].ls = merg(tre[a].ls,tre[b].ls,al,md);
	tre[a].rs = merg(tre[a].rs,tre[b].rs,md+1,ar);
	return upd(a),a;
}
int fa[MAXN],rt[MAXN];
int findf(int x) {return fa[x]==x ? x:(fa[x]=findf(fa[x]));}
void unionSet0(int a,int b) {fa[findf(a)] = findf(b);}
int unionSet(int a,int b,int ed) {
	int u = findf(a),v = findf(b);
	sum = 0;
	rt[v] = merg(rt[u],rt[v],0,1e8);
	sum = sum *1ll* ed % MOD;
	fa[u] = v; return sum;
}

int dfn2[MAXN],f2[MAXN][20],m2[MAXN][20],d2[MAXN],tim2;
void dfs0(int x,int ff,int fe) {
	d2[x] = d2[f2[x][0] = ff] + 1;
	m2[x][0] = fe;
	for(int i = 1;i <= 17;i ++) {
		f2[x][i] = f2[f2[x][i-1]][i-1];
		m2[x][i] = max(m2[x][i-1],m2[f2[x][i-1]][i-1]);
	}
	dfn2[x] = ++ tim2;
	for(int i = hd2[x];i;i = nx[i]) {
		if(v[i] != ff) {
			dfs0(v[i],x,w[i]);
		}
	}return ;
}
int lca(int a,int b) {
	if(d2[a] < d2[b]) swap(a,b);
	if(d2[a] > d2[b]) {
		for(int i = 17;i >= 0;i --) 
			if(d2[f2[a][i]] >= d2[b]) a = f2[a][i];
	} if(a == b) return a;
	for(int i = 17;i >= 0;i --) 
		if(f2[a][i] != f2[b][i]) 
			a = f2[a][i],b = f2[b][i];
	return f2[a][0];
}
int upmx(int a,int b) {
	int mx = 0;
	for(int i = 17;i >= 0;i --) {
		if(d2[f2[b][i]] >= d2[a]) mx = max(mx,m2[b][i]),b = f2[b][i];
	}return mx;
}
bool cmpdfn2(int a,int b) {return dfn2[a] < dfn2[b];}
set<pair<int,pair<int,int> > > se;
int ga[MAXN],cng;
int build(int *a,int le) {
	sort(a + 1,a + 1 + le,cmpdfn2);
	cng = 0;
	static int st[MAXN],tp;
	se.clear();
	st[tp = 1] = a[1];ga[cng = 1] = a[1];
	for(int i = 2;i <= le;i ++) {
		int p = 0,lc = lca(st[tp],a[i]);
		while(tp > 0 && d2[st[tp]] >= d2[lc]) {
			if(p) se.insert({upmx(st[tp],p),{st[tp],p}});
			p = st[tp --];
		}st[++ tp] = lc;
		if(p != lc) ga[++ cng] = lc,se.insert({upmx(lc,p),{lc,p}});
		if(lc != a[i]) st[++ tp] = a[i],ga[++ cng] = a[i];
	}
	int p = 0;
	while(tp > 0) {
		if(p) se.insert({upmx(st[tp],p),{st[tp],p}});
		p = st[tp --];
	}
	cnt = 0;
	for(int i = 1;i <= cng;i ++) fa[ga[i]] = ga[i],rt[ga[i]] = 0;
	return p;
}

bool FLAG = 0,f[MAXN];
int mx[MAXN],ar[MAXN],cna;
int siz[MAXN],SIZ,hv;
void dfs(int x,int ff,int fe) {
	if(FLAG) {
		mx[x] = fe;
		if(ff) mx[x] = max(mx[x],mx[ff]);
		ar[++ cna] = x;
	}
	siz[x] = 1; bool fl = 1;
	for(int i = hd1[x];i;i = nx[i]) {
		if(v[i] != ff && !f[v[i]]) {
			dfs(v[i],x,w[i]);
			siz[x] += siz[v[i]];
			if(siz[v[i]] > SIZ/2) fl = 0;
		}
	}
	if(fl && SIZ-siz[x] <= SIZ/2) hv = x;
	return ;
}
int calc(int x,int fe) {
	FLAG = 1; cna = 0;
	dfs(x,0,fe); FLAG = 0;
	build(ar,cna);
	for(int i = 1;i <= cna;i ++) {
		rt[ar[i]] = addtree(rt[ar[i]],mx[ar[i]],0,1e8);
	}
	int res = 0;
	while(!se.empty()) {
		int k = se.begin()->FI,tm;
		s = (se.begin()->SE).FI;o = (se.begin()->SE).SE;
		se.erase(se.begin());
		(res += (tm = unionSet(s,o,k))) %= MOD;
	}
	return res;
}
int solve(int x) {
	int res = calc(x,0); f[x] = 1;
	for(int i = hd1[x];i;i = nx[i]) {
		if(!f[v[i]]) {
			SIZ = siz[v[i]];
			(res += MOD - calc(v[i],w[i])) %= MOD;
			(res += solve(hv)) %= MOD;
		}
	}return res;
}
int main() {
	n = read();m = read();
	for(int i = 1;i <= n;i ++) fa[i] = i;
	for(int i = 1;i <= m;i ++) {
		U[i] = read();V[i] = read();W[i] = read();b[i] = i;
	}sort(b + 1,b + 1 + m,cmp);
	for(int i = 1;i <= m;i ++) {
		if(findf(U[b[i]]) != findf(V[b[i]])) {
			unionSet0(U[b[i]],V[b[i]]);
			ins(hd1,U[b[i]],V[b[i]],W[b[i]]);
			ins(hd1,V[b[i]],U[b[i]],W[b[i]]);
		}
	}
	for(int i = 1;i <= n;i ++) fa[i] = i;
	for(int i = 1;i <= m;i ++) {
		U[i] = read();V[i] = read();W[i] = read();b[i] = i;
	}sort(b + 1,b + 1 + m,cmp);
	for(int i = 1;i <= m;i ++) {
		if(findf(U[b[i]]) != findf(V[b[i]])) {
			unionSet0(U[b[i]],V[b[i]]);
			ins(hd2,U[b[i]],V[b[i]],W[b[i]]);
			ins(hd2,V[b[i]],U[b[i]],W[b[i]]);
		}
	}
	dfs0(1,0,0);
	SIZ = n; dfs(1,0,0);
	int ans = solve(hv);
	AIput(ans,'\n');
	return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值