[CodeChef] Maximum and Minimum

前言

写了我半个上午,白天剩下的时间都在调。

成功刷新代码长度上限:7k,270行。

题目

CodeChef

Vjudge

讲解

这路径权值显然就是最小生成树路径上的最大值,所以先跑 \(\tt Kruskal\)

我们思考如果只有一棵树怎么做,其实就是这道题,思路是差分,把点(LCA)处的权值转化为点到根的权值。

首先我们对 \(\tt G1\) 建一棵 \(\tt Kruskal\) 重构树,用全局平衡二叉树(重剖会多个 \(\log\))来支持修改与查询。

然后对 \(\tt G2\) 的最小生成树进行点分治,每次点分治把所有点取出按点到分治中心的路径权值从小到大排序。

依次在 \(\tt G1\) 中插入,然后询问权值即可。

时间复杂度 \(O(n\log_2n)\),常数略大。

代码

$\tt CodeChef$ 机子很快,爱了。
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std; 

typedef long long LL;
const int MAXN = 200005;
const int MOD = 998244353;
int n,m,Ans;

LL Read()
{
	LL x = 0,f = 1;char c = getchar();
	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

int head[2][MAXN],tot;
struct edge
{
	int v,w,nxt;
}e[MAXN<<2];
void Add_Edge(int opt,int u,int v,int w = 0)
{
	e[++tot] = edge{v,w,head[opt][u]};
	head[opt][u] = tot;
}
void Add_Double_Edge(int opt,int u,int v,int w = 0)
{
	Add_Edge(opt,u,v,w);
	Add_Edge(opt,v,u,w);
}

struct EDGE
{
	int u,v,w;
	void Get(){u = Read(),v = Read(),w = Read();}
	bool operator < (const EDGE &px)const{
		return w < px.w;
	}
}E[MAXN<<1];

int F[MAXN];//pot2 这玩意不能共享 
int findSet(int x)
{
	if(F[x] ^ x) F[x] = findSet(F[x]);
	return F[x];
}

int siz[MAXN],val[MAXN],son[MAXN],ppl[MAXN],d[MAXN],f[MAXN],cf[MAXN];//val : Kruskal重构树上的点权 
void dfs1(int x,int fa)
{
	d[x] = d[fa] + 1; siz[x] = 1; f[x] = fa;
	for(int i = head[0][x],v; i ;i = e[i].nxt)
		if((v = e[i].v) ^ fa)
		{
			dfs1(v,x);
			siz[x] += siz[v];
			if(siz[v] > siz[son[x]]) son[x] = v;
		}
	ppl[x] = siz[x] - siz[son[x]];
}
int tp[MAXN],dfn[MAXN],rdfn[MAXN],dfntot;
void dfs2(int x,int t) 
{
	tp[x] = t; rdfn[dfn[x] = ++dfntot] = x;//pot1
	if(!son[x]) return;
	dfs2(son[x],t);
	for(int i = head[0][x],v; i ;i = e[i].nxt)
		if((v = e[i].v) != f[x] && v != son[x])
			dfs2(v,v);
}

#define lc t[x].ch[0]
#define rc t[x].ch[1]
struct node
{
	int ch[2],s,cnt,lz,ans,sz,fa;
}t[MAXN];
void calc(int x,int v)
{
	if(!x) return;
	t[x].lz += v; if(t[x].lz >= MOD) t[x].lz -= MOD; //pot8
	t[x].cnt += v; if(t[x].cnt >= MOD) t[x].cnt -= MOD; //pot6
	t[x].ans = (t[x].ans + 1ll * v * t[x].s) % MOD;
}
void up(int x){t[x].ans = (t[lc].ans + t[rc].ans + 1ll * t[x].cnt * cf[x]) % MOD;}
void down(int x)
{
	if(!t[x].lz) return;
	calc(lc,t[x].lz);
	calc(rc,t[x].lz);
	t[x].lz = 0;
}
int tmp[MAXN],rt[MAXN];
void Build(int &x,int l,int r,int fa,int S)
{
	for(int i = l,now = 0;i <= r;++ i)
	{
		now += ppl[tmp[i]];
		if((now << 1) >= S)
		{
			x = tmp[i];//pot2
			t[x].s = (cf[x] + MOD) % MOD;//pot6
			t[x].sz = r-l+1; 
			if(l <= i-1) Build(lc,l,i-1,x,now-ppl[tmp[i]]);
			if(i+1 <= r) Build(rc,i+1,r,x,S-now);
			break;
		}
	}
	t[x].s = (0ll + t[x].s + t[lc].s + t[rc].s) % MOD; 
	if(t[x].s < 0) t[x].s += MOD; up(x);
}
void Build(int x)
{
	int S = 0,cur = 0;
	for(int u = x; u ;u = son[u]) tmp[++cur] = u,S += ppl[u];
	Build(rt[x],1,cur,f[x],S);
}
void Add(int x,int r,int v)//注意这棵全局平衡二叉树是dfn[x]有序 
{
	if(dfn[x]+t[rc].sz <= dfn[r]) {calc(x,v);return;}//pot9
	if(dfn[x] <= dfn[r]) t[x].cnt = (t[x].cnt + v) % MOD,t[x].ans = (t[x].ans + 1ll * v * cf[x]) % MOD;//pot5
	down(x);
	Add(lc,r,v); if(dfn[r] > dfn[x]) Add(rc,r,v);
	up(x);
}
int Query(int x,int r)
{
	if(dfn[x]+t[rc].sz <= dfn[r]) return t[x].ans;
	LL ret = 0; down(x);
	if(dfn[x] <= dfn[r]) ret = 1ll * t[x].cnt * cf[x];//pot3
	ret += Query(lc,r); if(dfn[r] > dfn[x]) ret += Query(rc,r);
	return ret % MOD;
}
void Add_To_Root(int x,int v)
{
	while(x)
	{
		Add(rt[tp[x]],x,v);
		x = f[tp[x]];
	}
}
int Query_To_Root(int x)
{
	int ret = 0;
	while(x)
	{
		ret = (ret + Query(rt[tp[x]],x)) % MOD;
		x = f[tp[x]];
	}
	return ret;
}

int MAX[MAXN],RT;
bool vis[MAXN];
void getrt(int x,int fa,int S)
{
	siz[x] = 1; MAX[x] = 0;//这波共享siz数组了 
	for(int i = head[1][x],v; i ;i = e[i].nxt)
		if(!vis[v = e[i].v] && v != fa)
		{
			getrt(v,x,S);
			siz[x] += siz[v];
			MAX[x] = Max(MAX[x],siz[v]);
		}
	MAX[x] = Max(MAX[x],S-siz[x]);
	if(!RT || MAX[x] < MAX[RT]) RT = x;
}
void getsiz(int x,int fa)
{
	siz[x] = 1;
	for(int i = head[1][x],v; i ;i = e[i].nxt)
		if(!vis[v = e[i].v] && v != fa)
		{
			getsiz(v,x);
			siz[x] += siz[v];
		}
}
int mxw[MAXN],pt;
void getpoint(int x,int fa)
{
	tmp[++pt] = x;
	for(int i = head[1][x],v; i ;i = e[i].nxt)
		if(!vis[v = e[i].v] && v != fa)
			mxw[v] = Max(mxw[x],e[i].w),getpoint(v,x);
}
LL tt;
void solve(int x,int fa,int sg)
{
	pt = 0; 
	for(int i = head[1][x],v; i ;i = e[i].nxt)
		if(!vis[v = e[i].v] && v != fa)
		{
			mxw[v] = e[i].w;
			if(fa) mxw[v] = Max(mxw[v],mxw[x]);//pot7
			getpoint(v,x);
		}
	sort(tmp+1,tmp+pt+1,[](int x,int y){if(mxw[x] ^ mxw[y]) return mxw[x] < mxw[y];return x < y;});
	Add_To_Root(x,sg);
	tt += pt;
	for(int i = 1;i <= pt;++ i)
	{
		Ans = (Ans + 1ll * mxw[tmp[i]] * Query_To_Root(tmp[i])) % MOD;
		Add_To_Root(tmp[i],sg);
	}
	Add_To_Root(x,MOD-sg);
	for(int i = 1;i <= pt;++ i) Add_To_Root(tmp[i],MOD-sg);
}
void dfs3(int x)
{
	vis[x] = 1; getsiz(x,0);
	solve(x,0,1);
	for(int i = head[1][x],v; i ;i = e[i].nxt)
		if(!vis[v = e[i].v]) solve(v,x,MOD-1);
	for(int i = head[1][x],v; i ;i = e[i].nxt)
		if(!vis[v = e[i].v])
			RT = 0,getrt(v,x,siz[v]),dfs3(RT);
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n = Read(); m = Read();
	//G1
	for(int i = 1;i <= m;++ i) E[i].Get();
	sort(E+1,E+m+1);
	for(int i = 1;i <= n;++ i) F[i] = i;
	for(int i = 1,U,V,now = n;i <= m;++ i)
		if((U = findSet(E[i].u)) ^ (V = findSet(E[i].v)))
		{
			++now; F[now] = now;
			Add_Double_Edge(0,now,U);//pot4
			Add_Double_Edge(0,now,V);
			F[U] = now; F[V] = now; val[now] = E[i].w;
		}
	dfs1((n<<1)-1,0); dfs2((n<<1)-1,(n<<1)-1);
	for(int i = 1;i < (n<<1);++ i)
	{
		cf[i] = val[i] - val[f[i]];
		if(cf[i] < 0) cf[i] += MOD;
	}
	for(int i = 1;i < (n<<1);++ i) if(rdfn[i] == tp[rdfn[i]]) Build(rdfn[i]);
	//G2
	for(int i = 1;i <= m;++ i) E[i].Get();
	sort(E+1,E+m+1);
	for(int i = 1;i <= n;++ i) F[i] = i;
	for(int i = 1,U,V;i <= m;++ i)
		if((U = findSet(E[i].u)) ^ (V = findSet(E[i].v)))
			Add_Double_Edge(1,E[i].u,E[i].v,E[i].w),F[U] = V;
	getrt(1,0,n); dfs3(RT);
	Put(Ans,'\n');
	return 0;
}
/*
首先我们对G1建一棵Kruskal重构树,用全局平衡二叉树(重剖多个log)来支持修改与查询。
然后对G2的最小生成树进行点分治,每次点分治把所有点取出按mxw[u]从小到大排序
小的用差分的形式在G2中插进去,然后用大的在G2中查询 
*/
posted @ 2022-02-17 16:56  皮皮刘  阅读(53)  评论(0编辑  收藏  举报