zoukankan      html  css  js  c++  java
  • 【WC2018】通道(边分治,虚树,动态规划)

    【WC2018】通道(边分治,虚树,动态规划)

    题面

    UOJ
    洛谷

    题解

    既然是三棵树,那么显然就是找点什么东西来套个三层。

    一棵树怎么做?入门dp。

    两棵树?假设在第一棵树中的深度为(dep)。在第一棵树中枚举(LCA),因为两点之间距离可以转化为两点深度和减去两倍(LCA)的深度,而已知当前的(LCA)是谁了。那么在第二棵树上的每个节点(u)下挂一个(u'),边权为(dep[u]),那么枚举(LCA)之后答案就是在当前(LCA)的不同子树中的两点((x)在第二棵树上的((x',y'))的距离最大值。而动态维护两点距离最大值,也就是动态维护直径,直径可以很简单的合并。那么只需要维护一下枚举到每个(LCA)的时候在第二棵树上的直径就好了。大概是线性的?

    然后三棵树?

    那么第一层就边分治解决。
    考虑过边((u,v))的贡献,假设在两侧子树中选择的点是((x,y))
    那么答案就是:(dis1(x,y)+dis2(x,y)+dis3(x,y))
    此时(dis1(x,y))已知,只需要考虑剩下的两个部分。
    因为这个计算同时涉及到两个点,因此我们不得不枚举点对。
    但是我们知道:(dis(x,y)=dis(root,x)+dis(root,y)-2dis(root,LCA(x,y))),假设到根的距离为(dep),就是(dis(x,y)=dep(x)+dep(y)-2dep(LCA))
    显然通过边分治我们可以知道单独与某个点相关的所有贡献,因此唯一需要考虑的就是剩下的(2dep2(LCA2(x,y))+2dep3(LCA3(x,y)))
    定义(w(x)=D(x)+dep2(x)+dep3(x)),其中(D)表示到达分治边的距离。
    那么现在要做的就是最大化(w(x)+w(y)-2dep2(LCA2(x,y))-2dep3(LCA3(x,y)))
    现在(w(x),w(y))在边分治之后已知,考虑计算后面两个东西。
    我们把当前分治所考虑的所有点给丢到(T2)上面构建虚树,这样子可以在(LCA2)处合并答案,那么我们在虚树上考虑在哪里合并,此时就是要找到当前点的不同子树中的((x,y)),而(dep(LCA2))因为在此合并答案,所以此时已知,所以要求的变成了最大化(w(x)+w(y)-2dep3(LCA3))
    似乎又看见了枚举(LCA)。然后回想起了两棵树的做法,于是又在(u)底下挂一个(u'),权值为(D(x)+dep2(x))。那么又变成了在虚树上枚举(LCA),然后求不同子树上的点在第三棵树上的((u',v'))的距离最大值了(QwQ)
    似乎说起来就这么多了。。。
    写起来瞬间(6k+)了。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define ll long long
    #define MAX 400100
    inline ll read()
    {
    	ll x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int lg[MAX];
    int S[2][MAX],top[2],type[MAX];
    ll W[MAX],ans;
    namespace Tree3
    {
    	struct Line{int v,next;ll w;}e[MAX<<1];
    	int h[MAX],cnt=1;
    	inline void Add(int u,int v,ll w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
    	int st[20][MAX],sum,fir[MAX];
    	int dep[MAX];ll dis[MAX];
    	void dfs(int u,int ff)
    	{
    		st[0][++sum]=u;fir[u]=sum;dep[u]=dep[ff]+1;
    		for(int i=h[u];i;i=e[i].next)
    			if(e[i].v!=ff)
    				dis[e[i].v]=dis[u]+e[i].w,dfs(e[i].v,u),st[0][++sum]=u;
    	}
    	int Compare(int a,int b){return dep[a]<dep[b]?a:b;}
    	void pre()
    	{
    		for(int j=1;j<=lg[sum];++j)
    			for(int i=1;i+(1<<j)-1<=sum;++i)
    				st[j][i]=Compare(st[j-1][i],st[j-1][i+(1<<(j-1))]);
    	}
    	int LCA(int u,int v)
    	{
    		u=fir[u];v=fir[v];if(u>v)swap(u,v);
    		int l=lg[v-u+1];
    		return Compare(st[l][u],st[l][v-(1<<l)+1]);
    	}
    	ll Dis(int u,int v){return W[u]+W[v]+dis[u]+dis[v]-2*dis[LCA(u,v)];}
    }
    
    namespace Tree2
    {
    	struct Line{int v,next;ll w;}e[MAX<<1];
    	int h[MAX],cnt=1;
    	inline void Add(int u,int v,ll w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
    	int st[20][MAX],sum;
    	int dfn[MAX],low[MAX],tim,fir[MAX];
    	int dep[MAX];ll dis[MAX];
    	void dfs(int u,int ff)
    	{
    		st[0][++sum]=u;dfn[u]=++tim;fir[u]=sum;dep[u]=dep[ff]+1;
    		for(int i=h[u];i;i=e[i].next)
    			if(e[i].v!=ff)
    				dis[e[i].v]=dis[u]+e[i].w,dfs(e[i].v,u),st[0][++sum]=u;
    		low[u]=tim;
    	}
    	int Compare(int a,int b){return dep[a]<dep[b]?a:b;}
    	void pre()
    	{
    		for(int j=1;j<=lg[sum];++j)
    			for(int i=1;i+(1<<j)-1<=sum;++i)
    				st[j][i]=Compare(st[j-1][i],st[j-1][i+(1<<(j-1))]);
    		memset(h,0,sizeof(h));cnt=1;
    	}
    	int LCA(int u,int v)
    	{
    		u=fir[u];v=fir[v];if(u>v)swap(u,v);
    		int l=lg[v-u+1];
    		return Compare(st[l][u],st[l][v-(1<<l)+1]);
    	}
    	struct Pair{int u,v;ll dis;}f[MAX][2];
    	Pair make(int u,int v,ll dis){if(u>v)swap(u,v);return (Pair){u,v,dis};}
    	Pair make(int u,int v){if(u>v)swap(u,v);return (Pair){u,v,Tree3::Dis(u,v)};}
    	bool operator<(Pair a,Pair b){if(!a.u)return true;if(!b.u)return false;return a.dis<b.dis;}
    	Pair operator+(Pair a,Pair b)
    	{
    		Pair ret=max(a,b);
    		ret=max(ret,max(make(a.u,b.u),make(a.u,b.v)));
    		ret=max(ret,max(make(a.v,b.u),make(a.v,b.v)));
    		return ret;
    	}
    	ll Calc(Pair a,Pair b){return max(max(make(a.v,b.u),make(a.v,b.v)),max(make(a.u,b.u),make(a.u,b.v))).dis;}
    	int Q[MAX<<2],T,Stack[MAX<<1];
    	bool cmp(int a,int b){return dfn[a]<dfn[b];}
    	bool vis[MAX];
    	void DP(int u,ll Pls)
    	{
    		f[u][0]=f[u][1]=make(0,0,0);
    		if(vis[u])f[u][type[u]]=make(u,u,0);
    		for(int i=h[u];i;i=e[i].next)
    		{
    			int v=e[i].v;DP(v,Pls);
    			ans=max(ans,max(Calc(f[u][0],f[v][1]),Calc(f[u][1],f[v][0]))+Pls-2*dis[u]);
    			f[u][0]=f[u][0]+f[v][0];
    			f[u][1]=f[u][1]+f[v][1];
    		}
    	}
    	void Solve(ll Pls)
    	{
    		T=0;cnt=1;
    		for(int i=1;i<=top[0];++i)Q[++T]=S[0][i];
    		for(int i=1;i<=top[1];++i)Q[++T]=S[1][i];
    		sort(&Q[1],&Q[T+1],cmp);
    		for(int i=1;i<=T;++i)W[Q[i]]+=dis[Q[i]];
    		for(int i=1;i<=T;++i)vis[Q[i]]=true;
    		for(int i=T;i>1;--i)Q[++T]=LCA(Q[i],Q[i-1]);
    		sort(&Q[1],&Q[T+1],cmp);T=unique(&Q[1],&Q[T+1])-Q-1;
    		for(int i=1,top=0;i<=T;++i)
    		{
    			while(top&&low[Stack[top]]<dfn[Q[i]])--top;
    			if(top)Add(Stack[top],Q[i],dis[Q[i]]-dis[Stack[top]]);Stack[++top]=Q[i];
    		}
    		DP(Q[1],Pls);
    		for(int i=1;i<=T;++i)if(vis[Q[i]])W[Q[i]]-=dis[Q[i]];
    		for(int i=1;i<=T;++i)vis[Q[i]]=false,h[Q[i]]=0;
    	}
    }
    
    namespace Tree1
    {
    	struct Line{int v,next;ll w;}e[MAX<<1];
    	int h[MAX],cnt=1;
    	inline void Add(int u,int v,ll w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
    	vector<int> son[MAX];
    	int n,N;ll V[MAX];
    	void dfs(int u,int ff)
    	{
    		for(int i=h[u];i;i=e[i].next)
    			if(e[i].v!=ff)son[u].push_back(e[i].v),V[e[i].v]=e[i].w,dfs(e[i].v,u);
    	}
    	void ReBuild()
    	{
    		cnt=2;memset(h,0,sizeof(h));
    		for(int i=1;i<=N;++i)
    		{
    			int l=son[i].size();
    			if(l<=2)
    				for(int j=0;j<l;++j)
    					Add(i,son[i][j],V[son[i][j]]),Add(son[i][j],i,V[son[i][j]]);
    			else
    			{
    				int ls=++N,rs=++N;
    				Add(i,ls,0);Add(ls,i,0);Add(i,rs,0);Add(rs,i,0);
    				for(int j=0;j<l;++j)
    					if(j&1)son[ls].push_back(son[i][j]);
    					else son[rs].push_back(son[i][j]);
    			}
    		}
    	}
    	bool vis[MAX];
    	int size[MAX],mx,rt;
    	ll dis[MAX];
    	void getroot(int u,int ff,int Size)
    	{
    		size[u]=1;
    		for(int i=h[u];i;i=e[i].next)
    		{
    			int v=e[i].v;if(vis[i>>1]||v==ff)continue;
    			getroot(v,u,Size);size[u]+=size[v];
    			int ret=max(size[v],Size-size[v]);
    			if(ret<mx)mx=ret,rt=i;
    		}
    	}
    	void dfs(int u,int ff,int opt)
    	{
    		if(u<=n)type[u]=opt,S[opt][++top[opt]]=u;
    		for(int i=h[u];i;i=e[i].next)
    			if(e[i].v!=ff&&!vis[i>>1])
    				dis[e[i].v]=dis[u]+e[i].w,dfs(e[i].v,u,opt);
    	}
    	void Divide(int u,int Size)
    	{
    		mx=1e9;getroot(u,0,Size);
    		if(mx>=1e9)return;
    		vis[rt>>1]=true;int nw=rt,SS=Size-size[e[rt].v];
    		top[0]=top[1]=dis[e[nw].v]=dis[e[nw^1].v]=0;
    		dfs(e[nw].v,0,0);dfs(e[nw^1].v,0,1);
    		for(int i=1;i<=top[0];++i)W[S[0][i]]+=dis[S[0][i]];
    		for(int i=1;i<=top[1];++i)W[S[1][i]]+=dis[S[1][i]];
    		Tree2::Solve(e[rt].w);
    		for(int i=1;i<=top[0];++i)W[S[0][i]]-=dis[S[0][i]];
    		for(int i=1;i<=top[1];++i)W[S[1][i]]-=dis[S[1][i]];
    		Divide(e[nw].v,size[e[nw].v]);
    		Divide(e[nw^1].v,SS);
    	}
    }
    int n;
    int main()
    {
    	n=read();Tree1::N=Tree1::n=n;
    	for(int i=2;i<=n+n;++i)lg[i]=lg[i>>1]+1;
    	for(int i=1;i<n;++i)
    	{
    		int u=read(),v=read();ll w=read();
    		Tree1::Add(u,v,w);Tree1::Add(v,u,w);
    	}
    	for(int i=1;i<n;++i)
    	{
    		int u=read(),v=read();ll w=read();
    		Tree2::Add(u,v,w);Tree2::Add(v,u,w);
    	}
    	for(int i=1;i<n;++i)
    	{
    		int u=read(),v=read();ll w=read();
    		Tree3::Add(u,v,w);Tree3::Add(v,u,w);
    	}
    	Tree2::dfs(1,0);Tree2::pre();
    	Tree3::dfs(1,0);Tree3::pre();
    	Tree1::dfs(1,0);Tree1::ReBuild();Tree1::Divide(1,Tree1::N);
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    django 定时任务 django-crontab 的使用
    Django中更新多个对象数据与删除对象的方法
    Django复制记录的方法
    Python中关于日期的计算总结
    django中添加日志功能
    Python 日期时间datetime 加一天,减一天,加减一小时一分钟,加减一年
    根据后端传的时间前端js进行倒计时
    输入pip命令报错:from pip import main ImportError: cannot import name 'main'
    操作uwsgi命令
    关于linux下安装mysqlclient报 Failed building wheel for mysqlclient问题
  • 原文地址:https://www.cnblogs.com/cjyyb/p/10193672.html
Copyright © 2011-2022 走看看