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


    题意:

    戳这里

    分析:

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

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

    每一次找到分治的中心边 (u o 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(nlog ))

    (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
    ",ans);
    	}
    
    
    }
    
    int main()
    {
    	zzc::work();
    	return 0;
    }
    
    
  • 相关阅读:
    错误滚动条eclipse快速定位到错误处
    路径原因linux下tomcat无法启动
    提示命令命令行将U盘文件系统转换成ntfs
    客户传真第四部分 个人理财风险防范8.当心银行汇款引发的诈骗
    请求错误[Python]网络爬虫(三):异常的处理和HTTP状态码的分类
    范围元【2013 GDCPC】有为杯 广东ACM省赛小总结
    编程程序国外程序员的BASIC情结——我的编程生涯始于BASIC
    执行取消利用timer实现的倒计时
    文件分析IDA反汇编/反编译静态分析iOS模拟器程序(二)加载文件与保存数据库
    安全业务第四部分 个人理财风险防范9.取款也要加小心
  • 原文地址:https://www.cnblogs.com/youth518/p/14254073.html
Copyright © 2020-2023  润新知