ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P4565 [CTSC2018]暴力写挂 边分治+虚树

2021-01-09 09:05:05  阅读:252  来源: 互联网

标签:rt sta P4565 dep top CTSC2018 lca 虚树 dis


题意:

戳这里

分析:

这道题是通道的弱化版,所以没什么好说的直接开始推柿子

我们发现存在两个 \(lca\) 边分治没法直接解决有根树过 \(lca\) 的路径,但是 \(dep(x)+dep(y)-dep(lca(x,y))\) 这个东西可以转化成 \(\frac{1}{2}\times (dep(x)+dep(y)+dis(x,y))\) 而 \(dis(x,y)\) 这种树上路径是可以通过树上分治得到的,由于点分治会将树一次划分成多个点,不好处理第二个 \(lca\) 而边分治在多叉转二叉之后,每次分治都只会产生两个点集,便于处理 \(lca\) 所以我们就考虑边分治

每一次找到分治的中心边 \(u\to v\),断开之后,按照常见的操作,对左右连通块分别染色,记 \(w(x)=dep_1(x)+dis(x,u/v)\) 这样答案转化成了求 \(ans=w(x)+w(y)-dep_2(lca(x,y))+k\) 其中 \(k\) 表示分治边的边权,是个定值,所以我们要最大化 \(w(x)+w(y)-dep_2(lca)\) 我们发现这个东西很类似于树的直径,可以 \(dp\) 求得,所以我们的任务转化成,在 第二棵树上找一个点对,满足两个点的颜色不同,同时尽可能最大化 \(w(x)+w(y)-dep_2(lca)\) 由于每次染色的点集是有限的,所以我们考虑建出虚树,避免无用的枚举,同时建虚树的时候使用 \(O(1)lca\) 降低复杂度,这样复杂度就变成了 \(O(n\log )\)

\(tip:\)

  1. 虚树上 \(dp\) 时极小值一定要足够小/kk
  2. 染色时只染原树上有的点,不然对于多叉转二叉建的虚点,在第二棵树上不存在\(lca\)
  3. 最后一定要特判 \(x==y\) 的情况

代码:

毒瘤题,我连写带调花了两天,重构了好几遍/kk

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second

using namespace std;

namespace zzc
{
	inline int read()
	{
		int x=0,f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
	
	typedef long long ll;
	const int maxn = 4e5+5;
	const ll inf = 1e18+7;
	int n;
	int s[maxn][2],num[2],col[maxn],lg[maxn<<1];
	ll w[maxn],ans=-inf;
	
	namespace t2
	{
		struct edge
		{
			int to,nxt,val;
		}e[maxn<<1];
		int cnt=1,idx,top,tot;
		int head[maxn],dep[maxn],dfn[maxn],st[maxn<<1][20],sta[maxn],q[maxn<<1];
		ll dis[maxn],f[maxn][2];
		bool vis[maxn];

		bool cmp(int x,int y)
		{
			return dfn[x]<dfn[y];
		}
		
		void add(int u,int v,int w)
		{
			e[++cnt].to=v;
			e[cnt].val=w;
			e[cnt].nxt=head[u];
			head[u]=cnt;
		}
		
		void dfs(int u,int ff)
		{
			dfn[u]=++idx;dep[u]=dep[ff]+1;st[idx][0]=u;
			for(int i=head[u];i;i=e[i].nxt) if(e[i].to!=ff) dis[e[i].to]=dis[u]+e[i].val,dfs(e[i].to,u),st[++idx][0]=u;
		}
		
		int Min(int x,int y)
		{
			return dep[x]<dep[y]?x:y;
		}
		
		int lca(int x,int y)
		{
			if(dfn[x]>dfn[y]) swap(x,y);
			ll k=lg[dfn[y]-dfn[x]+1];
			return Min(st[dfn[x]][k],st[dfn[y]-(1<<k)+1][k]);
		}
		
		long long calc(int x,int y)
		{
			return dis[x]+dis[y]-(dis[lca(x,y)]<<1);
		}
		
		void init()
		{
			int a,b,c;
			for(ll i=1;i<n;i++)
			{
				a=read();b=read();c=read();
				add(a,b,c);add(b,a,c);
			}
			dfs(1,0);
			for(int j=1;j<=19;j++)
			{
				for(int i=1;i+(1<<j)-1<=idx;i++)
				{
					st[i][j]=Min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
				}
			}
			memset(head,0,sizeof(head));
		}
		
		void DP(int u,ll k)
		{
			f[u][0]=f[u][1]=-inf;
			if(vis[u])f[u][col[u]]=w[u];
			for(int i=head[u];i;i=e[i].nxt)
			{
				int v=e[i].to;
				DP(v,k);
				ans=max(ans,k+max(f[u][0]+f[v][1],f[u][1]+f[v][0])-2*dis[u]);
				f[u][0]=max(f[u][0],f[v][0]);
				f[u][1]=max(f[u][1],f[v][1]);
			}
			head[u]=0;vis[u]=false;
		}
		
		void solve(ll k)
		{
			cnt=0;tot=0;
			for(int i=1;i<=num[0];++i) q[++tot]=s[i][0];
			for(int i=1;i<=num[1];++i) q[++tot]=s[i][1];
			for(int i=1;i<=tot;++i) vis[q[i]]=true;
			sort(&q[1],&q[tot+1],cmp);
			top=0;if(q[1]!=1)sta[++top]=1;
			for(int i=1;i<=tot;++i)
			{
				int u=q[i],ff=lca(u,sta[top]);
				while(top>1&&dep[sta[top-1]]>=dep[ff]) add(sta[top-1],sta[top],0),--top;
				if(ff!=sta[top]) add(ff,sta[top],0),sta[top]=ff;
				sta[++top]=u;
			}
			while(top>1) add(sta[top-1],sta[top],0),--top;
			DP(1,k);
		}
	}
	
	namespace t1
	{
		struct edge
		{
			int to,nxt,val;
		}e[maxn<<3];
		int head[maxn<<2],tmp[maxn<<2],siz[maxn<<2];
		ll dis[maxn<<2],dep[maxn<<2];
		vector<int> son[maxn<<2];
		int N,cnt=0,rt,mx;
		bool vis[maxn<<2];
		
		void add(int u,int v,int w)
		{
			e[++cnt].to=v;
			e[cnt].val=w;
			e[cnt].nxt=head[u];
			head[u]=cnt;
		}
		
		void dfs(int u,int ff)
		{
			for(int i=head[u];i;i=e[i].nxt)
				if(e[i].to!=ff) son[u].push_back(e[i].to),dep[e[i].to]=dep[u]+e[i].val,tmp[e[i].to]=e[i].val,dfs(e[i].to,u);
		}
		
		void rebuild()
		{
			memset(head,0,sizeof(head));cnt=1;
			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],tmp[son[i][j]]),add(son[i][j],i,tmp[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]);
					}
				}
			}
		}

		void getroot(int u,int ff,int Size)
		{
			siz[u]=1;
			for(int i=head[u];i;i=e[i].nxt)
			{
				int v=e[i].to;
				if(v==ff||vis[i>>1])continue;
				getroot(v,u,Size);
				siz[u]+=siz[v];
				int ret=max(siz[v],Size-siz[v]);
				if(ret<mx)mx=ret,rt=i;
			}
		}
		
		void dfs(int u,int ff,int opt)
		{
			if(u<=n)s[++num[opt]][opt]=u,col[u]=opt;
			for(int i=head[u];i;i=e[i].nxt)
				if(e[i].to!=ff&&!vis[i>>1])
					dis[e[i].to]=dis[u]+e[i].val,dfs(e[i].to,u,opt);
		}
		
		void divide(int u,int Size)
		{
			mx=1e9;getroot(u,0,Size);
			if(mx>=1e9)return;
			vis[rt>>1]=true;
			int old=rt,res=Size-siz[e[rt].to];
			num[0]=num[1]=dis[e[rt].to]=dis[e[rt^1].to]=0;
			dfs(e[rt].to,0,0);dfs(e[rt^1].to,0,1);
			if(!num[0]&&!num[1])return;
			for(int i=1;i<=num[0];++i) w[s[i][0]]+=dis[s[i][0]]+dep[s[i][0]];
			for(int i=1;i<=num[1];++i) w[s[i][1]]+=dis[s[i][1]]+dep[s[i][1]];
			t2::solve(e[rt].val);
			for(int i=1;i<=num[0];++i) w[s[i][0]]-=dis[s[i][0]]+dep[s[i][0]];
			for(int i=1;i<=num[1];++i) w[s[i][1]]-=dis[s[i][1]]+dep[s[i][1]];
			divide(e[old].to,siz[e[old].to]);
			divide(e[old^1].to,res);
		}
		
		void init()
		{
			int a,b,c;
			for(ll i=1;i<N;i++)
			{
				a=read();b=read();c=read();
				add(a,b,c);add(b,a,c);
			}
			dfs(1,0);rebuild();
		}
		
	}
	
	void work()
	{
		lg[0]=-1;for(int i=1;i<=800000;i++) lg[i]=lg[i>>1]+1;
		n=read();
		t1::N=n;
		t1::init();
		t2::init();
		t1::divide(1,t1::N);
		ans/=2;
		for(int i=1;i<=n;i++) ans=max(ans,t1::dep[i]-t2::dis[i]);
		printf("%lld\n",ans);
	}


}

int main()
{
	zzc::work();
	return 0;
}

标签:rt,sta,P4565,dep,top,CTSC2018,lca,虚树,dis
来源: https://www.cnblogs.com/youth518/p/14254073.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有