zoukankan      html  css  js  c++  java
  • CF1336F Journey

    \(\color{#FF003F}{\texttt {CF1336F Journey}}\)

    对两条链的 \(\operatorname {lca}\) 是否相同进行分类讨论。下面 \(x\) 的链指 \(\operatorname {lca}(s,t)=x\) 的链,链 \((s,t)\) 需要满足 \(dfn_s<dfn_t\)

    1. 如果\(\operatorname {lca}\)不同。

    dfs整颗树,并在 \(\operatorname {lca}\) 的深度较浅处产生贡献。这样做的好处是 两条链的交一定是 在深度较深的链上的一条以\(\operatorname {lca}\)为端点的链,树状树组统计。
    具体地,dfs到一个点 \(x\) 时,先递归处理儿子,再统计 \(x\) 和子树内的链的贡献。
    放张官方sol的图。

    图中有两条链,E-F和G-H,他们的交是B-G,且B-C和B-D的距离是k。
    处理G-H的时候,对C,D做子树加,在E-F统计答案的时候,查询E,F的值。

    1. 如果 \(\operatorname {lca}\) 相同。

    枚举每个点作为\(\operatorname {lca}\)。当前点是 \(x\)。将 \(x\) 的所有链的 \(s\) 端点建棵虚树,\(t\) 端点存在 \(s\) 上。
    \(\operatorname {lca}(x1,x2)=a\),从 \(a\)\(y1\)\(k\) 步到点 \(u\),所有合法的 \(y2\)\(u\) 的子树内。
    启发式合并+线段树合并维护。

    图中有两条链,B-E和C-F,他们的交是A-D,且A-X的距离是k。
    \(\operatorname {lca}(B,C)=A\),A向E走 \(k\) 步到X,查询X的子树内的 \(y\) 点,点F产生贡献。

    1. 这样做还遗漏了一种情况

    图中有2条链,A-E和C-D,他们的交是B-X。
    其中 \(dfn_A<dfn_E<dfn_D<dfn_C\),容易发现这对链没被上面两种情况包含。

    对于这种情况,把当前点的链按 \(dfn_s\) 排序,此时链的交一定是从lca向下的一条链,用类似第1种情况的方法统计即可。

    复杂度 \(O(mlog^2n+nlogn)\),瓶颈在于启发式合并。

    // Author -- xyr2005
    
    #include<bits/stdc++.h>
    
    #define lowbit(x) ((x)&(-(x)))
    #define DEBUG fprintf(stderr,"Running on Line %d in Function %s\n",__LINE__,__FUNCTION__)
    #define SZ(x) ((int)x.size())
    #define mkpr std::make_pair
    #define pb push_back
    
    typedef long long ll;
    typedef unsigned int uint;
    typedef unsigned long long ull;
    typedef std::pair<int,int> pi;
    typedef std::pair<ll,ll> pl;
    using std::min;
    using std::max;
    
    const int inf=0x3f3f3f3f,Inf=0x7fffffff;
    const ll INF=0x3f3f3f3f3f3f3f3f;
    
    std::mt19937 rnd(std::chrono::steady_clock::now().time_since_epoch().count());
    template <typename _Tp>_Tp gcd(const _Tp &a,const _Tp &b){return (!b)?a:gcd(b,a%b);}
    template <typename _Tp>inline _Tp abs(const _Tp &a){return a>=0?a:-a;}
    template <typename _Tp>inline void chmax(_Tp &a,const _Tp &b){(a<b)&&(a=b);}
    template <typename _Tp>inline void chmin(_Tp &a,const _Tp &b){(b<a)&&(a=b);}
    template <typename _Tp>inline void read(_Tp &x)
    {
    	char ch(getchar());bool f(false);while(!isdigit(ch)) f|=ch==45,ch=getchar();
    	x=ch&15,ch=getchar();while(isdigit(ch)) x=(((x<<2)+x)<<1)+(ch&15),ch=getchar();
    	f&&(x=-x);
    }
    template <typename _Tp,typename... Args>inline void read(_Tp &t,Args &...args){read(t);read(args...);}
    inline int read_str(char *s)
    {
    	char ch(getchar());while(ch==' '||ch=='\r'||ch=='\n') ch=getchar();
    	char *tar=s;*tar=ch,ch=getchar();while(ch!=' '&&ch!='\r'&&ch!='\n'&&ch!=EOF) *(++tar)=ch,ch=getchar();
    	return tar-s+1;
    }
    
    const int N=150005;
    int n;
    struct edge{
    	int v,nxt;
    }c[N<<1];
    int front[N],edge_cnt;
    inline void addedge(int u,int v)
    {
    	c[++edge_cnt]=(edge){v,front[u]};
    	front[u]=edge_cnt;
    }
    int anc[N][21],dep[N],siz[N],dfn[N],rev[N],id;
    struct seg_tr{
    	struct Node{
    		int ls,rs,sum;
    	}f[N<<5];
    	int node_cnt;
    	int st[N<<5],top;
    	inline void PushUp(int x){f[x].sum=f[f[x].ls].sum+f[f[x].rs].sum;}
    	inline int newnode()
    	{
    		int cur=top?st[top--]:++node_cnt;
    		f[cur]=(Node){0,0,0};
    		return cur;
    	}
    	void Update(int &cur,int l,int r,int pos)
    	{
    		if(!cur) cur=newnode();
    		++f[cur].sum;
    		if(l==r) return;
    		int mid=(l+r)>>1;
    		if(pos<=mid) Update(f[cur].ls,l,mid,pos);
    		else Update(f[cur].rs,mid+1,r,pos);
    	}
    	int Query(int L,int R,int l,int r,int cur)
    	{
    		if(!cur) return 0;
    		if(L<=l&&r<=R) return f[cur].sum;
    		int mid=(l+r)>>1;
    		return (L<=mid?Query(L,R,l,mid,f[cur].ls):0)+(R>mid?Query(L,R,mid+1,r,f[cur].rs):0);
    	}
    	int merge(int a,int &b)
    	{
    		if(!a||!b) return a|b;
    		f[a].sum+=f[b].sum;
    		f[a].ls=merge(f[a].ls,f[b].ls);
    		f[a].rs=merge(f[a].rs,f[b].rs);
    		st[++top]=b,b=0;
    		return a;
    	}
    	void del(int &x)
    	{
    		if(!x) return;
    		del(f[x].ls),del(f[x].rs);
    		st[++top]=x,x=0;
    	}
    }tr;
    int Fa[N];
    void dfs1(int x,int fa)
    {
    	dep[x]=dep[fa]+1,anc[x][0]=fa,Fa[x]=fa,siz[x]=1;
    	for(int i=1;i<=20;++i) anc[x][i]=anc[anc[x][i-1]][i-1];
    	dfn[x]=++id,rev[x]=id;
    	for(int i=front[x];i;i=c[i].nxt)
    	{
    		int v=c[i].v;
    		if(v!=fa) dfs1(v,x),siz[x]+=siz[v];
    	}
    }
    int jump(int x,int k)
    {
    	for(int i=20;i>=0;--i) if((k>>i)&1)	x=anc[x][i];
    	return x;
    }
    int lca(int x,int y)
    {
    	if(dep[x]<dep[y]) std::swap(x,y);
    	for(int i=20;i>=0;--i) if(dep[anc[x][i]]>=dep[y]) x=anc[x][i];
    	if(x==y) return x;
    	for(int i=20;i>=0;--i) if(anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i];
    	return anc[x][0];
    }
    ll ans;
    int k;
    struct BIT{
    	int c[N];
    	inline void clear(){memset(c,0,sizeof(c));}
    	inline void add(int x,int C){++x;for(;x<N;x+=lowbit(x))c[x]+=C;}
    	inline int sum(int x){++x;int ans=0;for(;x;x-=lowbit(x))ans+=c[x];return ans;}
    }_tr;
    struct node{
    	int x,y;
    	inline bool operator < (const node &o)const{return dfn[x]<dfn[o.x];}
    };
    std::vector<node> v[N];
    void dfs2(int x,int fa)
    {
    	for(int i=front[x];i;i=c[i].nxt)
    	{
    		int v=c[i].v;
    		if(v!=fa) dfs2(v,x);
    	}
    	for(auto it:v[x]) ans+=_tr.sum(dfn[it.x])+_tr.sum(dfn[it.y]);
    	for(auto it:v[x])
    	{
    		if(dep[it.x]-dep[x]>=k)
    		{
    			int qwq=jump(it.x,dep[it.x]-dep[x]-k);
    			_tr.add(dfn[qwq],1),_tr.add(dfn[qwq]+siz[qwq],-1);
    		}
    		if(dep[it.y]-dep[x]>=k)
    		{
    			int qwq=jump(it.y,dep[it.y]-dep[x]-k);
    			_tr.add(dfn[qwq],1),_tr.add(dfn[qwq]+siz[qwq],-1);
    		}
    	}
    }
    int t[N],pos,st[N],top;
    std::vector<int> e[N],q[N];
    int root[N];
    void ins(int x)
    {
    	if(!top||(dfn[x]>=dfn[st[top]]&&dfn[x]<dfn[st[top]]+siz[st[top]]))
    	{
    		t[++pos]=x,st[++top]=x;
    		return;
    	}
    	int l=lca(x,st[top]);
    	while(top>1&&dfn[st[top-1]]>=dfn[l]) e[st[top-1]].pb(st[top]),--top;
    	if(st[top]!=l) e[l].push_back(st[top]),st[top]=l,t[++pos]=l;
    	st[++top]=x,t[++pos]=x;
    }
    int cur_node;
    std::vector<int> in[N];
    void dfs3(int x)
    {
    	std::function<void(int)> merge=[&](int a)
    	{
    		if(in[a].size()>in[x].size()) std::swap(in[a],in[x]),std::swap(root[a],root[x]);
    		for(auto it:in[a])
    		{
    			int qwq;
    			if(dep[x]-dep[cur_node]>=k) qwq=cur_node;
    			else
    			{
    				int len=dep[x]+dep[it]-(dep[cur_node]<<1);
    				if(len<k) continue;
    				qwq=jump(it,len-k);
    			}
    			ans+=tr.Query(dfn[qwq],dfn[qwq]+siz[qwq]-1,1,n,root[x]);
    		}
    		for(auto it:in[a]) in[x].push_back(it);
    		in[a].clear();
    		root[x]=tr.merge(root[x],root[a]);
    	};
    	for(auto it:q[x])
    	{
    		int qwq;
    		if(dep[x]-dep[cur_node]>=k) qwq=cur_node;
    		else
    		{
    			int len=dep[x]+dep[it]-(dep[cur_node]<<1);
    			if(len>=k) qwq=jump(it,len-k);
    			else qwq=0;
    		}
    		if(qwq) ans+=tr.Query(dfn[qwq],dfn[qwq]+siz[qwq]-1,1,n,root[x]);
    		tr.Update(root[x],1,n,dfn[it]);
    		in[x].push_back(it);
    	}
    	for(auto it:e[x]) dfs3(it),merge(it);
    }
    void solve(int x)
    {
    	cur_node=x,top=0,pos=0;
    	std::vector<int> nd;
    	for(auto it:v[x]) nd.pb(it.x),q[it.x].pb(it.y);
    	nd.erase(std::unique(nd.begin(),nd.end()),nd.end());
    	for(auto it:nd) ins(it);
    	while(top>1) e[st[top-1]].pb(st[top]),--top;
    	int minn=inf,id=0;
    	for(int i=1;i<=pos;++i) if(dfn[t[i]]<minn) minn=dfn[t[i]],id=t[i];
    	if(id) dfs3(id);
    	for(auto it:v[x]) q[it.x].clear();
    	for(int i=1;i<=pos;++i) tr.del(root[t[i]]),in[t[i]].clear(),e[t[i]].clear();
    }
    void _solve(int x)
    {
    	std::vector<int> tmp;
    	for(auto it:v[x])
    	{
    		ans+=_tr.sum(dfn[it.x]);
    		if(dep[it.y]-dep[x]>=k)
    		{
    			int qwq=jump(it.y,dep[it.y]-dep[x]-k);
    			_tr.add(dfn[qwq],1),_tr.add(dfn[qwq]+siz[qwq],-1);
    			tmp.push_back(qwq);
    		}
    	}
    	for(auto qwq:tmp) _tr.add(dfn[qwq],-1),_tr.add(dfn[qwq]+siz[qwq],1);
    }
    int main()
    {
    	int m;read(n,m,k);
    	int x,y;
    	for(int i=1;i<n;++i) read(x,y),addedge(x,y),addedge(y,x);
    	dfs1(1,0);
    	for(int i=1;i<=m;++i)
    	{
    		read(x,y);
    		if(dfn[x]>dfn[y]) std::swap(x,y);
    		v[lca(x,y)].pb((node){x,y});
    	}
    	dfs2(1,0);
    	_tr.clear();
    	for(int i=1;i<=n;++i) std::sort(v[i].begin(),v[i].end());
    	for(int i=1;i<=n;++i) _solve(i);
    	for(int i=1;i<=n;++i) solve(i);
    	printf("%lld\n",ans);
    	return 0;
    }
    
    

    转载请注明出处

    https://www.cnblogs.com/xyr2005/

  • 相关阅读:
    1074 食物链 (并查集)
    2832 6个朋友
    病毒 (拓扑)
    4735 烦人的幻灯片 (拓扑)
    JavaScript中变量的LHS引述和RHS引用
    td自动换行
    SQL Server 中的 NOLOCK 到底是什么意思?
    jQuery中遇到的坑
    jQuery中attr()函数 VS prop()函数
    Javascript刷新页面的几种方法
  • 原文地址:https://www.cnblogs.com/xyr2005/p/12719197.html
Copyright © 2011-2022 走看看