zoukankan      html  css  js  c++  java
  • 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;
    }
    
    珍爱生命,远离抄袭!
  • 相关阅读:
    29
    28
    27
    950. 按递增顺序显示卡牌
    25
    20190624
    409. 最长回文串
    636. 函数的独占时间
    LeetCode 1046. 最后一块石头的重量(1046. Last Stone Weight) 50
    LeetCode 942. 增减字符串匹配(DI String Match) 49
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/solution-p5327.html
Copyright © 2011-2022 走看看