zoukankan      html  css  js  c++  java
  • 【洛谷6072】『MdOI R1』Path(树上无交点双路径问题)

    题目链接

    • 给定一棵 (n) 个点的无根树,每条边有一个边权。
    • ​要求选出两条无交点的树上路径,使得两条路径边权异或和之和最大。
    • (2le nle3 imes10^4)

    树上无交点双路径问题

    这类问题一种可以认为是套路的解决方案是枚举一个分界点 (x),让这两个路径一条在 (x) 子树内,一条在 (x)子树外。

    对于这道题,首先是一个经典转化,令 (v_x) 表示从 (x) 到根所有边权异或和,那么路径 ((x,y)) 的边权异或和就是 (v_xoplus v_y)

    (x) 子树内的情况可以 DSU on Tree+Trie 求解。

    (x) 子树外的情况,考虑我们利用 Trie 求出任意一对异或值最大的点对 ((A,B)),那么除了 (A) 到根的路径和 (B) 到根的路径上的点,都满足 ((A,B)) 这条路径在子树外。而对于这两条路径,我们直接从根节点走到 (A/B),每次把不在子树内的点加入 Trie,可以维护出这些点子树外的最大异或值。

    坑掉的单 (log) 做法

    实际上,更精细地实现可以做到完全一只 (log)

    首先改成以 ((A,B)) 路径上某个节点为根。

    若分界点不在 ((A,B)) 路径上,由于确定了子树外的路径,我们实际上没有必要对每个点求解子树内的答案,而是只去求解与这条路径相邻的点的子树内(它们比子树节点内包含更多路径,肯定更优),这样一来每个点就只会被计算一次。

    若分界点在 ((A,B)) 路径上,发现之前求解它子树外答案的做法本来就是一只 (log) 的。而子树内的答案可以仿照这个做法,反过来从 (A/B) 向上,每次把子树内不在 Trie 中的点加入 Trie 即可。

    但由于是在已经用两只 (log) 的做法通过此题后才知道这个做法的,所以不想再写了。

    代码:(O(nlog nlog V))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Rg register
    #define RI Rg int
    #define Cn const
    #define CI Cn int&
    #define I inline
    #define W while
    #define N 30000
    #define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].v=z)
    using namespace std;
    int n,o,ans,A,B,V,G[N+5],ee,lnk[N+5];struct edge {int to,nxt,v;}e[N<<1];map<int,int> id;
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	char oc,FI[FS],*FA=FI,*FB=FI;
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    }using namespace FastIO;
    int rt;struct Trie
    {
    	#define New() (Et?Ep[Et--]:++Nt)
    	#define Cls(x) (O[x].Sz=O[x].S[0]=O[x].S[1]=0)
    	int Nt,Et,Ep[N*30];struct node {int Sz,S[2];}O[N*30];I void Cl() {W(Nt) Cls(Nt),--Nt;rt=Et=0;}//情况
    	I void A(int& rt,CI x,CI d=29) {!rt&&(rt=New()),++O[rt].Sz,~d&&(A(O[rt].S[x>>d&1],x,d-1),0);}//加元素
    	I void D(int& rt,CI x,CI d=29) {~d&&(D(O[rt].S[x>>d&1],x,d-1),0),!--O[rt].Sz&&(Cls(rt),Ep[++Et]=rt,rt=0);}//删元素
    	I int Q(CI rt,CI x,CI d=29) {if(!~d) return 0;RI t=x>>d&1;return O[rt].S[t^1]?Q(O[rt].S[t^1],x,d-1)|(1<<d):Q(O[rt].S[t],x,d-1);}//询问与x的最大异或值
    }T;
    int v[N+5],sz[N+5],f[N+5],g[N+5];I void dfs(CI x) {sz[x]=1;for(RI i=lnk[x],y;i;i=e[i].nxt)
    	(y=e[i].to)^f[x]&&(v[y]=v[f[y]=x]^e[i].v,dfs(y),sz[x]+=sz[e[i].to],sz[e[i].to]>sz[g[x]]&&(g[x]=e[i].to));}
    #define Ins(v) (T.A(rt,v),o=max(o,T.Q(rt,v)))//把一个点加入Trie树,同时更新最大异或和
    I void Tg(CI x) {Ins(v[x]);for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^f[x]&&(Tg(e[i].to),0);}//加入x子树内的点
    I void Dl(CI x) {T.D(rt,v[x]);for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^f[x]&&(Dl(e[i].to),0);}//删除x子树内的点
    I void Wk(CI x,CI t=0) {x^1&&(Wk(f[x],x),0);G[x]=o,Ins(v[x]);for(RI i=lnk[x],y;i;i=e[i].nxt) (y=e[i].to)^f[x]&&y^t&&(Tg(y),0);}//求出x到根所有点子树外的答案
    I void DSU(CI x) {RI i,y;for(i=lnk[x];i;i=e[i].nxt) (y=e[i].to)^f[x]&&y^g[x]&&(DSU(y),Dl(y),o=0);//先求解轻儿子,做一个删一个
    	if(g[x]) for(DSU(g[x]),i=lnk[x];i;i=e[i].nxt) (y=e[i].to)^f[x]&&y^g[x]&&(Tg(y),0);Ins(v[x]),x^1&&(ans=max(ans,o+(~G[x]?G[x]:V)),0);}//求解重儿子,加上轻儿子,加上当前点,以当前点为分界点更新答案
    int main()
    {
    	RI i,x,y,z;for(read(n),i=1;i^n;++i) read(x,y,z),add(x,y,z),add(y,x,z);
    	for(dfs(1),V=-1,i=1;i<=n;++i) G[i]=-1,id[v[i]]=i,T.A(rt,v[i]),(x=T.Q(rt,v[i]))>V&&(A=i,B=id[v[i]^x],V=x);//任意求出一条异或值最大的路径
    	return T.Cl(),o=0,Wk(A),T.Cl(),o=0,Wk(B),T.Cl(),o=0,DSU(1),printf("%d
    ",ans),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    xps插入图片
    xps文件的基本操作
    大家注意:升级 win8.1 火狐浏览器 谷歌浏览器 搜狗五笔输入法 都不能用啦
    CF4C_Registration system 题解
    CF1B_Spreadsheets 题解
    CSP-J/S 初赛知识点整理
    Nodejs在centos下的安装
    sqlserver2012 表分区
    adb unknown host service 这个问题的解决,转载
    char和nchar,varchar和nvarchar的区别(转载)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6072.html
Copyright © 2011-2022 走看看