• [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中查询 
    */
    
  • 相关阅读:
    discuz 帖子模块用到的表及自动发帖函数
    【转】php json_encode中文为空的解决办法
    linux自定义脚本添加到rc.local脚本无法正常运行的问题
    【转】实战 SSH 端口转发
    linux shell脚本守护进程监控svn服务
    解决ecshop登陆自动退出的莫名现象
    windows下不打开浏览器访问网页的方法
    【转】windows下安装和调用curl的方法
    调用discuz编辑器发布帖子显示html代码的解决办法
    linux下搭建svn版本控制软件
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15905409.html
Copyright © 2020-2023  润新知