• P5327


    究极巨大神仙题。。。。(刚学线段树合并就做这么难的题啊?——小 c)

    我们知道序列数点对一般离不开两种方法:cdq 分治和扫描线(就是对每个 (r) 数有多少个 (l))。对应到树上的话,cdq 分治就是点分治,扫描线变异出两种:枚举 lca 和枚举点对中的一个点。这题我们采用第三种方法。

    (x) 可以开展贸易活动的点显然是所有经过 (x)((u,v)) 的并。我们考虑对每个点都维护一个数据结构维护 ((u,v)) 们的并,这样对每个 ((u,v)) 我们都要在所有 (xin(u,v)) 的数据结构上做 ((u,v)) 链加。考虑树上差分,这样问题转化为对该数据结构做一遍子树和。那么容易想到可以用线段树合并维护,此时由于有链加,要树剖,总复杂度 2log。而且线段树合并上还要打懒标记 / 标记永久化,比较麻烦。

    考虑优化。容易发现,对每个 (x)​,包含 (x)​ 的 ((u,v))​ 的并是一个包含 (x)​ 的连通块。感性理解发现其实就是所有 (u,v)​ 的虚树。证明(考场上就不证明了吧,太显然了):考虑两个 ((u_1,v_1),(u_2,v_2))​,它们都包含 (x)​,必然相交。设交为 ((u_3,v_3))​,不妨设 (u_1,u_2)​ 靠近 (u_3)​,(v_1,v_2)​ 靠近 (v_3)​,那么显然 ((u_1,u_2)=(u_1,u_3)cup(u_2,u_3)subseteq(u_1,v_1)cup(u_2,v_2))​,((v_1,v_2),(u_1,v_2),(u_2,v_1))​ 同理。所以说所有 ((u,v))​ 的并其实就是所有 (u,v)​ 两两之间的路径并,那么显然就是所有 (u,v)​ 的虚树。

    我们现在想知道虚树的大小。这是一个很经典的 trick(参考 P3320)——动态维护虚树大小。解决这个问题并不需要把虚树建出来、把边连起来,只要想象模拟 dfs 的过程,将点们按 dfn 排序得到 (v),那么所有 (v_i o v_{imod |v|+1}) 会恰好把虚树的每条边访问两遍。所以我们只需要求相邻点的距离和即可。

    现在我们想知道 (x)​ 的若干子树内的 (u,v)​ 集合并起来之后(顺便加入 (x)​ 的 todo-list),相邻点距离和等于多少。考虑线段树合并,区间维护 dfn 落在该区间内的相邻点距离和,上传只需要再维护区间内最左和最右点。线段树合并只要考虑在一满一空时和两叶子是是否可行(其他时候上传),前者直接移植,后者直接搞。总复杂度 1log。注意叶子如果被累加多次要真的累加多次(反正不影响答案),因为有减法操作。

    code 还挺好写的
    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    const int N=200010,LOG_N=20;
    int n,m;
    vector<int> nei[N];
    vector<int> add[N],del[N];
    int fa[N];
    int euler[N],fst[N],noweuler,dep[N],dfn[N],nowdfn,mng[N];
    void dfs(int x=1){
    	euler[++noweuler]=x;fst[x]=noweuler;
    	dfn[x]=++nowdfn;mng[nowdfn]=x;
    	for(int i=0;i<nei[x].size();i++){
    		int y=nei[x][i];
    		if(y==fa[x])continue;
    		dep[y]=dep[x]+1;
    		fa[y]=x;
    		dfs(y);
    		euler[++noweuler]=x;
    	}
    }
    bool cmp(int x,int y){return dep[x]<dep[y];}
    struct stable{
    	int _log[N],mn[N][LOG_N];
    	void init(){
    		for(int i=2;i<=2*n;i++)_log[i]=_log[i-1]+(1<<_log[i-1]+1==i);
    		for(int i=1;i<2*n;i++)mn[i][0]=euler[i];
    		for(int j=1;j<LOG_N;j++)for(int i=1;i+(1<<j)-1<2*n;i++)mn[i][j]=min(mn[i][j-1],mn[i+(1<<j-1)][j-1],cmp);
    	}
    	int _mn(int l,int r){
    		int log0=_log[r-l+1];
    		return min(mn[l][log0],mn[r-(1<<log0)+1][log0],cmp);
    	}
    }st;
    int lca(int x,int y){
    	x=fst[x],y=fst[y];
    	if(x>y)swap(x,y);
    	return st._mn(x,y);
    }
    int dis(int x,int y){
    	x=mng[x],y=mng[y];
    	int a=lca(x,y);
    	return dep[x]+dep[y]-2*dep[a];
    }
    struct segtree{
    	int sz,root[N];
    	struct node{int lson,rson,cnt,sum,lft,rit;}nd[N<<5];
    	#define lson(p) nd[p].lson
    	#define rson(p) nd[p].rson
    	#define cnt(p) nd[p].cnt
    	#define sum(p) nd[p].sum
    	#define lft(p) nd[p].lft
    	#define rit(p) nd[p].rit
    	int nwnd(){return nd[++sz]=node({0,0,0,0,0,0}),sz;}
    	void init(){
    		sz=0,nd[0]=node({0,0,0,0,0,0});
    		for(int i=1;i<=n;i++)root[i]=nwnd();
    	}
    	void sprup(int p){
    		sum(p)=sum(lson(p))+sum(rson(p))+(rit(lson(p))&&lft(rson(p))?dis(rit(lson(p)),lft(rson(p))):0);
    		lft(p)=lft(lft(lson(p))?lson(p):rson(p));rit(p)=rit(rit(rson(p))?rson(p):lson(p));
    	}
    	void add(int x,int v,int p,int tl=1,int tr=n){
    		if(tl==tr)return cnt(p)+=v,lft(p)=rit(p)=cnt(p)?tl:0,void();
    		int mid=tl+tr>>1;
    		if(x<=mid){
    			if(!lson(p))lson(p)=nwnd();
    			add(x,v,lson(p),tl,mid);
    		}
    		else{
    			if(!rson(p))rson(p)=nwnd();
    			add(x,v,rson(p),mid+1,tr);
    		}
    		sprup(p);
    	}
    	int mrg(int p,int q,int tl=1,int tr=n){
    		if(!p||!q)return p|q;
    		if(tl==tr)return cnt(p)+=cnt(q),lft(p)=rit(p)=cnt(p)?tl:0,p;
    		int mid=tl+tr>>1;
    		lson(p)=mrg(lson(p),lson(q),tl,mid),rson(p)=mrg(rson(p),rson(q),mid+1,tr);
    		return sprup(p),p;
    	}
    }segt;
    long long ans;
    void dfs0(int x=1){
    	for(int i=0;i<add[x].size();i++)segt.add(dfn[add[x][i]],1,segt.root[x]);
    	for(int i=0;i<del[x].size();i++)segt.add(dfn[del[x][i]],-1,segt.root[x]);
    	for(int i=0;i<nei[x].size();i++){
    		int y=nei[x][i];
    		if(y==fa[x])continue;
    		dfs0(y);
    		segt.root[x]=segt.mrg(segt.root[x],segt.root[y]);
    	}
    	int p=segt.root[x];
    	ans+=(segt.sum(p)+(segt.lft(p)?dis(segt.lft(p),segt.rit(p)):0))/2;
    }
    int main(){
    	cin>>n>>m;
    	for(int i=1;i<n;i++){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		nei[x].pb(y),nei[y].pb(x);
    	}
    	dep[1]=1;dfs();
    	st.init();
    	while(m--){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		int a=lca(x,y);
    		add[x].pb(x),add[x].pb(y);
    		add[y].pb(x),add[y].pb(y);
    		del[a].pb(x),del[a].pb(y);
    		del[fa[a]].pb(x),del[fa[a]].pb(y);
    	}
    	segt.init();
    	dfs0();
    	cout<<ans/2;
    	return 0;
    }
    
    珍爱生命,远离抄袭!
  • 相关阅读:
    外校培训前三节课知识集合纲要(我才不会告诉你我前两节只是单纯的忘了)
    floyd算法----牛栏
    bfs开始--马的遍历
    (DP 线性DP 递推) leetcode 64. Minimum Path Sum
    (DP 线性DP 递推) leetcode 63. Unique Paths II
    (DP 线性DP 递推) leetcode 62. Unique Paths
    (DP 背包) leetcode 198. House Robber
    (贪心 复习) leetcode 1007. Minimum Domino Rotations For Equal Row
    (贪心) leetcode 452. Minimum Number of Arrows to Burst Balloons
    (字符串 栈) leetcode 921. Minimum Add to Make Parentheses Valid
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/solution-p5327.html
Copyright © 2020-2023  润新知