zoukankan      html  css  js  c++  java
  • @atcoder


    @description@

    给定一个 N 个点的树,编号为 1, 2, ..., N。第 i 条边连接 ai 与 bi。
    再给定一个长度为 N 的 01 串,第 i 个字符表示 i 号点上是否有一个棋子。
    保证至少有一个点有棋子。

    你可以执行如下操作若干次:
    选择两个距离 >= 2 的棋子,将这两个棋子往靠近对方的方向分别移动 1 条边。

    是否可以通过若干次操作将所有棋子集中于一个点。如果可以,求出最小操作次数。

    Constraints
    2≤N≤2000
    |S|=N,只包含 0, 1 且至少有一个 1。
    1≤ai,bi≤N(ai≠bi) 且形成一棵树。

    Input
    输入形式如下:
    N
    S
    a1 b1
    a2 b2
    :
    aN−1 bN−1

    Output
    如果不可能集中所有棋子,输出 -1;
    否则输出最小操作次数。

    Sample Input 1
    7
    0010101
    1 2
    2 3
    1 4
    4 5
    1 6
    6 7
    Sample Output 1
    3

    @solution@

    N 这么小,显然可以枚举最后集中的点,然后进行判断。
    考虑规定了集中的点后,怎么快速判断是否合法。我们尝试去找操作下的不变量。

    显然操作要分成两类:祖先关系与非祖先关系。
    祖先关系的操作会将一个点拉低,一个点抬高;另一种操作会同时将两个点抬高。
    那么深度和的奇偶性是不会变的。

    同时发现两种操作下,深度和要么不变,要么减少 2。而当深度和 = 0 时所有点集中于一点。
    那么我们可以忽略祖先操作,只剩下非祖先操作,那么深度和每次严格减少 2。只要可行,最小操作次数 = 深度和 / 2。
    当然需要稍微证明一下这个的合法性:祖先关系的操作总可以与下一个操作交换。于是可以将所有祖先操作挪到最后。然后就没用了。

    怎么判断是否可行呢?考虑根,如果只对根的不同子树进行操作都可以合法的话自然合法。
    求出根的每棵子树的深度和。相当于每次操作选出两棵子树,它们的深度和分别减一。

    若干正整数 a1, a2, ..., ak,每次操作同时选两个数减一,问是否可以全部减成 0。这其实是一个很经典的问题。
    解决方法是比较 sum - max{ai} 与 max{ai}。如果 sum - max{ai} < max{ai},显然最后会剩下 max{ai} - (sum - max{ai});否则,一定可以消到只剩 0 或 1(取决于 sum{ai} 的奇偶性)。

    假如只操作根就是这样。
    假如还可以操作其他子树内的点,我们显然应该递归去 max{ai} 的那一棵子树,看最后能够得到最小深度和。
    再将最终得到的最小深度和拿去上面的判定条件判断一下,就可以知道当前能够剩下的最小深度和。

    @accepted code@

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int MAXN = 2000;
    const int INF = (1<<30);
    struct edge{
    	int to; edge *nxt;
    }edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt=&edges[0];
    void addedge(int u, int v) {
    	edge *p = (++ecnt);
    	p->to = v, p->nxt = adj[u], adj[u] = p;
    	p = (++ecnt);
    	p->to = u, p->nxt = adj[v], adj[v] = p;
    }
    int a[MAXN + 5], N;
    int f[MAXN + 5], s[MAXN + 5];
    void dfs(int x, int fa) {
    	s[x] = a[x], f[x] = 0;
    	for(edge *p=adj[x];p;p=p->nxt) {
    		if( p->to == fa ) continue;
    		dfs(p->to, x), s[x] += s[p->to];
    		f[x] += f[p->to] + s[p->to];
    	}
    }
    int get(int x, int fa) {
    	int mx = 0;
    	for(edge *p=adj[x];p;p=p->nxt) {
    		if( p->to == fa ) continue;
    		if( mx == 0 || f[p->to] > f[mx] ) mx = p->to;
    	}
    	if( mx == 0 ) return 0;
    	int k = get(mx, x) + s[mx];
    	if( f[x] - f[mx] - s[mx] >= k ) return f[x] & 1;
    	else return k - (f[x] - f[mx] - s[mx]);
    }
    char S[MAXN + 5];
    int main() {
    	scanf("%d%s", &N, S + 1);
    	for(int i=1;i<=N;i++) a[i] = S[i] - '0';
    	for(int i=1;i<N;i++) {
    		int u, v; scanf("%d%d", &u, &v);
    		addedge(u, v);
    	}
    	int ans = INF;
    	for(int j=1;j<=N;j++) {
    		dfs(j, 0);
    		if( get(j, 0) == 0 )
    			ans = min(ans, f[j]/2);
    	}
    	if( ans == INF ) printf("-1
    ");
    	else printf("%d
    ", ans);
    }
    

    @details@

    惊了,这一场 AGC 的过题人数 E > F > D。

    其实 E 还是比较容易往正解想。而且就算不会那个经典的模型,你也可以使用树形 dp 来搞定。

  • 相关阅读:
    atitit.为什么java体系开发效率这样低的原因and解决
    使用11g DNFS建立基于DNFS的tablespace
    MalformedObjectNameException: Invalid character '' in value part of property
    Spring MVC DispatcherServlet绑定多种URL
    chrome与pdf的事情
    JSP获取绝对物理地址
    spring mvc 与 jasper Report集成
    HttpServletRequest和ServletRequest的区别
    aJax请求结果中包含form的问题
    javascript与java编码互转
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11735367.html
Copyright © 2011-2022 走看看