zoukankan      html  css  js  c++  java
  • AtCoder NOMURA 2020 题解

    题目传送门:AtCoder NOMURA Programming Competition 2020

    A - Study Scheduling

    略。

    #include <cstdio>
    
    int main() {
    	int H1, M1, H2, M2, K;
    	scanf("%d%d%d%d%d", &H1, &M1, &H2, &M2, &K);
    	printf("%d
    ", (H2 - H1) * 60 + M2 - M1 - K);
    	return 0;
    }
    

    B - Postdocs

    易证存在一种最优解为把所有 ? 替换成 D

    #include <cstdio>
    
    char str[200005];
    
    int main() {
    	scanf("%s", str + 1);
    	for (int i = 1; str[i]; ++i)
    		printf("%c", str[i] == '?' ? 'D' : str[i]);
    	puts("");
    	return 0;
    }
    

    C - Folia

    (C_d) 为最优方案中深度为 (d) 的非叶子节点数量。

    则有限制 (C_d le 2 C_{d - 1} - A_d)(C_d le C_{d + 1} + A_{d + 1})

    (d = 0)(d = N) 向中心推进限制即可。

    #include <cstdio>
    
    typedef long long LL;
    const int MN = 200005;
    
    int N;
    LL A[MN], B[MN], C[MN];
    
    int main() {
    	scanf("%d", &N);
    	for (int i = 0; i <= N; ++i) scanf("%lld", &A[i]);
    	if (!N) return puts(A[0] == 1 ? "1" : "-1"), 0;
    	for (int i = N; i >= 0; --i) B[i] = B[i + 1] + A[i];
    	C[0] = 1 - A[0];
    	for (int i = 1; i <= N; ++i) {
    		C[i] = 2 * C[i - 1] - A[i];
    		if (C[i] < 0 || C[i] + A[i] == 0) return puts("-1"), 0;
    		if (C[i] + A[i] > B[i]) C[i] = B[i] - A[i];
    	}
    	LL Ans = 0;
    	for (int i = 0; i <= N; ++i) Ans += A[i] + C[i];
    	printf("%lld
    ", Ans);
    	return 0;
    }
    

    D - Urban Planning

    假设原图由 (M) 个基环内向树和 (K) 个内向树构成。
    并且令 (K) 个内向树的大小依次为 (b_1, b_2, ldots , b_K)

    要求将给定点对连通的最小边数,也就相当于求 (N) 减去连通块个数。
    只要求出所有图中的连通块个数,再用 (N {(N - 1)}^K) 减去这个值即可。

    显然至少有 (M) 个连通块,给总连通块个数贡献 (M {(N - 1)}^K)

    在其余的 (K) 个内向树中,可以将每个内向树看作一个整体,每在其中形成一个环,就为连通块个数贡献 (1)

    考虑枚举某个环中的所有内向树,假设为 (k)(k ge 1))个,为 ({ a_1, a_2, ldots , a_k })(无顺序)。
    那么它们形成环的方案数就为 (displaystyle (k - 1)! prod_{i = 1}^{k} b_{a_i})

    即枚举一种圆排列(共有 ((k - 1)!) 种),每一个内向树的根可以连向下一个内向树的任何一个点,所以把所有 (b) 值相乘即可。

    除了一种例外情况,当 (k = 1) 时,方案数应该为 ((b_{a_1} - 1)),因为有不能连向自己的限制。

    据此可以考虑一个 DP:对于每个 (k) 满足 (1 le k le K),求出在所有内向树中选出 (k) 个时它们的 (b) 值的乘积之和。

    依次加入每个内向树即可维护。

    求出后每项乘上 ((k - 1)!),再乘上 ({(N - 1)}^{K - k})(剩下的其它内向树的方案数)贡献给答案即可,对于 (k = 1) 特殊处理一下。

    #include <cstdio>
    #include <vector>
    
    typedef long long LL;
    const int Mod = 1000000007;
    const int MN = 5005;
    
    inline int qPow(int b, int e) {
    	int a = 1;
    	for (; e; e >>= 1, b = (LL)b * b % Mod)
    		if (e & 1) a = (LL)a * b % Mod;
    	return a;
    }
    
    int N, M, A[MN], Fac[MN];
    std::vector<int> G[MN];
    int K, B[MN];
    
    int vis[MN];
    void DFS(int u) {
    	++B[K], vis[u] = 1;
    	for (int v : G[u]) DFS(v);
    }
    void DFS2(int u, int o) {
    	vis[u] = 1;
    	for (int v : G[u])
    		if (!vis[v]) DFS2(v, o);
    		else if (v == o) ++M;
    }
    
    int dp[MN][MN];
    
    int main() {
    	scanf("%d", &N), Fac[0] = 1;
    	for (int i = 1; i <= N; ++i) scanf("%d", &A[i]), Fac[i] = (LL)Fac[i - 1] * i % Mod;
    	for (int i = 1; i <= N; ++i) if (~A[i]) G[A[i]].push_back(i);
    	for (int i = 1; i <= N; ++i) if (!~A[i]) ++K, DFS(i);
    	for (int i = 1; i <= N; ++i) if (!vis[i]) DFS2(i, i);
    	dp[0][0] = 1;
    	for (int i = 1; i <= K; ++i) {
    		dp[i][0] = 1;
    		for (int j = 1; j <= i; ++j)
    			dp[i][j] = (dp[i - 1][j] + (LL)dp[i - 1][j - 1] * B[i]) % Mod;
    	}
    	dp[K][1] -= K;
    	int Ans = (LL)M * qPow(N - 1, K) % Mod;
    	for (int j = 1; j <= K; ++j) Ans = (Ans + (LL)dp[K][j] * Fac[j - 1] % Mod * qPow(N - 1, K - j)) % Mod;
    	Ans = ((LL)N * qPow(N - 1, K) % Mod - Ans + Mod) % Mod;
    	printf("%d
    ", Ans);
    	return 0;
    }
    

    E - Binary Programming

    (N = |T|),等价于执行以下操作 (N) 次:

    • 所有奇数位的数之和贡献给答案。
    • 删去任意一位。

    首先可以发现当 0 还没删光时,删 1 是不优的。

    然而当 0 删光了的时候,之后的操作对答案的贡献就很显然了。所以这里考虑如何删 0 是最优的。

    可以发现如果有两个相邻的 1,那么每次操作时它们对答案的贡献都是 (1)。所以可以先不考虑。

    那么一直删除相邻的两个 1 后,只剩下 (k)1 时,就形成了 (a_0mathtt{0} quad mathtt{1} quad a_1mathtt{0} quad mathtt{1} quad cdots quad mathtt{1} quad a_kmathtt{0}) 的情况。

    对于其中的第 (i)1,它最多给答案贡献 ((a_0 + cdots + a_{i - 1})) 的一半(取整方式取决于位置的奇偶性)加 ((a_i + cdots + a_k))

    而事实上这个上界是可以被达到的,也就是需要对每个 1 都满足在删它后面的 0 时它的位置在奇数:
    先把 (a_0) 个最开始的 0 删光,然后删 (a_1 - 1)0 留下恰好一个,以此类推,删掉 (a_{k - 1} - 1)0 留下恰好一个。
    这时形如 (mathtt{1010100000000 cdots}),然后从右到左把所有的 0 删掉即可。

    所以使用上述结论求答案即可。

    #include <cstdio>
    #include <cstring>
    
    typedef long long LL;
    const int MN = 200005;
    
    int N, C0, C1, K;
    char T[MN];
    int stk[MN], cnt[MN], tp;
    LL Ans;
    
    int main() {
    	scanf("%s", T + 1), N = strlen(T + 1);
    	for (int i = 1; i <= N; ++i) ++(T[i] & 1 ? C1 : C0);
    	Ans = (LL)(C1 + 1) * (C1 + 1) / 4;
    	T[0] = T[N + 1] = '0';
    	for (int i = 0; i <= N + 1; ++i) {
    		int x = T[i] - '0';
    		if (!x) {
    			if (tp && !stk[tp]) ++cnt[tp];
    			else stk[++tp] = 0, cnt[tp] = 1;
    		} else {
    			if (tp && stk[tp]) --tp, Ans += C0;
    			else stk[++tp] = 1, cnt[tp] = 1;
    		}
    	} --cnt[1], --cnt[tp];
    	for (int i = 1, s = 0; 2 * i <= tp; ++i)
    		s += cnt[2 * i - 1],
    		Ans += (s + i + 1) / 2 - (i + 1) / 2 + (C0 - s);
    	printf("%lld
    ", Ans);
    	return 0;
    }
    

    F - Sorting Game

    注意到,一个序列是合法的,当且仅当对于所有的两个下标 (i, j)(1 le i < j le M)),都有:
    按二进制位从高到低比较 (a_i)(a_j),出现 (a_i) 该位为 (1)(a_j) 该位为 (0) 后,其余的更低位 (a_i) 必须与 (a_j) 完全相同。

    考虑 (a_1 sim a_M) 的最高位,有两种情况:

    1. 依次为形如 (mathtt{000111}),也就是先 (0)(1):不存在两个下标先 (1)(0),转化为 ((N - 1, M)) 的子问题,共有 ((M + 1)) 种方案。
    2. 依次为形如 (mathtt{0001????0111}),也就是删去前缀连续的 (0) 和后缀连续的 (1) 后非空的情况:
      所以删去后的那一部分的更低位必须都全部相同了,而前缀连续的 (0) 和后缀连续的 (1) 则不受任何影响。
      这就相当于把中间的部分缩成同一个数了,假设缩完之后的长度为 (i),则转化为 ((N - 1, i)) 的子问题。
      假设缩完之后的长度为 (i),实际上有恰好 (i) 种方案去分配被缩起来的部分,而对于 (mathtt{????}) 部分,共有 (2^{M - i - 1}) 种方案。

    也就是说,有如下 DP 转移方程:

    [egin{aligned} mathrm{f}[n][m] &= (m + 1) cdot mathrm{f}[n - 1][m] + sum_{i = 1}^{m - 1} i cdot 2^{m - i - 1} cdot mathrm{f}[n - 1][i] end{aligned} ]

    后面的部分可以使用前缀和的思想以加速处理,时间复杂度为 (mathcal O (N M))

    #include <cstdio>
    #include <vector>
    
    typedef long long LL;
    const int Mod = 1000000007;
    const int MN = 5005;
    
    int N, M, f[MN][MN];
    
    int main() {
    	scanf("%d%d", &N, &M);
    	for (int j = 1; j <= M; ++j) f[0][j] = 1;
    	for (int i = 1; i <= N; ++i) {
    		for (int j = 2; j <= M; ++j)
    			f[i][j] = (2 * f[i][j - 1] + (LL)f[i - 1][j - 1] * (j - 1)) % Mod;
    		for (int j = 1; j <= M; ++j)
    			f[i][j] = (f[i][j] + (LL)f[i - 1][j] * (j + 1)) % Mod;
    	}
    	printf("%d
    ", f[N][M]);
    	return 0;
    }
    
  • 相关阅读:
    How to function call using 'this' inside forEach loop
    jquery.validate.unobtrusive not working with dynamic injected elements
    Difference between jQuery.extend and jQuery.fn.extend?
    Methods, Computed, and Watchers in Vue.js
    Caution using watchers for objects in Vue
    How to Watch Deep Data Structures in Vue (Arrays and Objects)
    Page: DOMContentLoaded, load, beforeunload, unload
    linux bridge
    linux bridge
    EVE-NG网卡桥接
  • 原文地址:https://www.cnblogs.com/PinkRabbit/p/AtCoderNOMURA2020.html
Copyright © 2011-2022 走看看