zoukankan      html  css  js  c++  java
  • 【AGC034E】Complete Compress

    题目

    题目链接:https://atcoder.jp/contests/agc034/tasks/agc034_e
    给你一颗 (n) 个节点的树,并用二进制串告诉你哪些节点上有棋子(恰好一颗)。
    可以进行若干次操作,每次操作可以将两颗距离至少为 (2) 的棋子向中间移动一步。
    问能否通过若干次操作使得所有的棋子都在一个点上,如果能,输出最小操作次数,如果不能,输出 (-1)
    (2 leq nleq 2000)

    思路

    考虑枚举最终集合的点 (rt)。显然所有点到达 (rt) 所需要的操作次数为以 (rt) 为根的树时

    [frac{sum_{i=1}^{n} mathrm{dep_i}}{2} ]

    我们只需要 check 是否有方案使得所有点到达 (rt) 即可。
    考虑树形 dp。设 (f_i) 表示所有点要到达 (rt) 时,(i) 子树内的点最多能操作多少次。
    假设当前处理到 (x) 的子树,我们可以把一个 (x) 子树内的点 (y) 拆成 (mathrm{dep}_y-mathrm{dep}_x) 个点。那么最多的操作次数等于每次在剩余的点中选择两个不同的删去,最多能操作的次数。
    (mathrm{sum}_x) 表示所有拆出来的点的数量,(mathrm{maxs}_x) 表示 (x) 所有儿子中 (mathrm{sum}_y+mathrm{siz}_y) 的最大值。
    如果 (2mathrm{maxs}_xleq mathrm{sum_x}),我们不难构造出一种方案使得最终最多剩余一个点(每次删除剩余数量最多的两个点)。所以此时

    [f_x=lfloorfrac{mathrm{sum}_x}{2} floor ]

    如果 (2mathrm{maxs}_x> mathrm{sum_x}),那么我们可以操作的次数就是 (mathrm{sum}_x-mathrm{maxs}_x+f_v),其中 (v)(mathrm{sums}_x) 对应的子节点。
    但是因为 (v) 子树内是需要自己匹配的,其最好情况下的匹配数量才能达到上式,而其内部匹配会导致 (f_x) 有一个上界 (lceilfrac{mathrm{sum}_x}{2} ceil)。所以此时

    [f_x=min(mathrm{sum}_x-mathrm{maxs}_x+f_v,lceilfrac{mathrm{sum}_x}{2} ceil) ]

    如果最后 (2f_{rt}=frac{sum_{i=1}^{n} mathrm{dep_i}}{2}),那么 (rt) 有解。
    时间复杂度 (O(n^2))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=2010,Inf=1e9;
    int n,ans,deps,tot,f[N],son[N],dep[N],maxs[N],sum[N],siz[N],head[N];
    bool flag[N];
    
    struct edge
    {
    	int next,to;
    }e[N*2];
    
    void add(int from,int to)
    {
    	e[++tot]=(edge){head[from],to};
    	head[from]=tot;
    }
    
    void dfs(int x,int fa)
    {
    	dep[x]=dep[fa]+1;
    	if (flag[x]) deps+=dep[x]-1;
    	siz[x]=flag[x]; maxs[x]=sum[x]=son[x]=0;
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (v!=fa)
    		{
    			dfs(v,x);
    			siz[x]+=siz[v]; sum[x]+=sum[v]+siz[v];
    			maxs[x]=max(maxs[x],sum[v]+siz[v]);
    			if (sum[v]+siz[v]>sum[son[x]]+siz[son[x]]) son[x]=v;
    		}
    	}
    	if (maxs[x]*2<=sum[x]) f[x]=sum[x]/2;
    		else f[x]=min(sum[x]-maxs[x]+f[son[x]],sum[x]-sum[x]/2);
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++)
    		scanf("%1d",&flag[i]);
    	for (int i=1,x,y;i<n;i++)
    	{
    		scanf("%d%d",&x,&y);
    		add(x,y); add(y,x);
    	}
    	ans=Inf;
    	for (int i=1;i<=n;i++)
    	{
    		tot=deps=0; dfs(i,0);
    		if (f[i]*2==deps) ans=min(ans,f[i]);
    	}
    	if (ans==Inf) printf("-1");
    		else printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    一网友推荐的书:框架设计(第2版):CLR Via C#
    线程与WinForm设计,防冻结,卡住窗体
    通用数据库操作辅助类DbHelper
    高级着色器语言(High Level Shader Language,简称HLSL)
    无法将 匿名方法 转换为类型“System.Delegate”,因为它不是委托类型
    根据RGB,计算灰度值
    温故而知新:WinForm/Silverlight多线程编程中如何更新UI控件的值
    [原创视频]PHP在netbeans中的简单使用
    自己封装的ASP.NET的SQLITE数据库的操作类
    《JavaScript征途》读后感
  • 原文地址:https://www.cnblogs.com/stoorz/p/14396896.html
Copyright © 2011-2022 走看看