VP Educational Codeforces Round 9


A. Grandma Laura and Apples

题意:一开始你有一个n,然后经过若干次运算,直到n=0,如果当前n是偶数,则直接除2,获得n2×m的钱,如果当前n是奇数,则获得n2×m的钱,n=n2,保证m是偶数,现在给出每个运算时是奇数还是偶数,求一共赚了多少钱。

先通过反推求出n来,最后一个的时候n一定等于1,那么如果当前运算前是奇数,n=n×2+1,否则n=n×2。然后再正着模拟一遍就行。

点击查看代码
void solve() {
	int n, m;
	std::cin >> n >> m;
	std::vector<std::string> s(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> s[i];
	}

	std::reverse(s.begin(), s.end());
	i64 sum = 1;
	for (int i = 1; i < n; ++ i) {
		if (s[i] == "halfplus") {
			sum = sum * 2 + 1;
		} else {
			sum = sum * 2;
		}
	}	

	i64 ans = 0;
	std::reverse(s.begin(), s.end());
	for (int i = 0; i < n; ++ i) {
		if (s[i] == "halfplus") {
			ans = ans + sum / 2 * m + m / 2;
		} else {
			ans = ans + sum / 2 * m;
		}

		sum /= 2;
	}

	std::cout << ans << "\n";
}

B. Alice, Bob, Two Teams

题意:给你一个AB串,每个位置都有一个价值,你可以获得所有b位置上的价值,现在你可以将一个前缀或者一个后缀位置上的数都改变,求最大价值。

预处理一个ab的价值前缀和,然后枚举翻转的前缀和后缀就行。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    std::string s;
    std::cin >> s;
    std::vector<i64> suma(n + 1), sumb(n + 1);
    for (int i = 0; i < n; ++ i) {
    	suma[i + 1] = suma[i] + (s[i] == 'A' ? a[i] : 0);
    	sumb[i + 1] = sumb[i] + (s[i] == 'B' ? a[i] : 0);
    }

    i64 ans = sumb[n];
    for (int i = n; i >= 1; -- i) {
    	ans = std::max(ans, sumb[i - 1] + suma[n] - suma[i - 1]);
    }

    for (int i = 1; i <= n; ++ i) {
    	ans = std::max(ans, sumb[n] - sumb[i] + suma[i]);
    }

    std::cout << ans << "\n";
}

C. The Smallest String Concatenation

题意:给你n个字符串,你要把它们拼接成一个字符串,使得这个字符串字典序最小。

直接排序即可,排序判断就是直接看谁在前面是组成的字符串更小。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<std::string> s(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> s[i];
    }

    std::sort(s.begin(), s.end(), [&](std::string & s, std::string & t) {
    	return s + t < t + s;
    });
    for (auto & x : s) {
    	std::cout << x;
    }
    std::cout << "\n";
}

D. Longest Subsequence

题意:给你一个数组,你要选尽可能多少数使得它们的lcm小于等于m,求最多选多少数。

发现m只有1e6,那么可以预处理每个数的因子,然后把每个数的倍数加一,这样就得到了每个数作为lcm是可以选多少数,从小到大枚举即可。注意如果是2,2,4,那么48的值都是3,但实际上这三个数的lcm不是8,但我们是从小到大枚举的,所以会选择4

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    std::vector<std::vector<int>> factor(m + 1);
    for (int i = 1; i <= m; ++ i) {
    	for (int j = i; j <= m; j += i) {
    		factor[j].push_back(i);
    	}
    }

    std::vector<int> cnt(m + 1);
    for (int i = 0; i < n; ++ i) {
    	if (a[i] <= m) {
    		cnt[a[i]] += 1;
    	}
    }

    std::vector<int> sum(m + 1);
    for (int i = 1; i <= m; ++ i) {
    	for (auto & j : factor[i]) {
    		sum[i] += cnt[j];
    	}
    }

    int max = 0, p = 0;
    for (int i = 1; i <= m; ++ i) {
    	if (sum[i] > max) {
    		max = sum[i];
    		p = i;
    	}
    }

    if (p == 0) {
    	std::cout << 1 << " " << 0 << "\n";
    	return;
    }

    std::vector<int> ans;
    for (int i = 0; i < n; ++ i) {
    	if (p % a[i] == 0) {
    		ans.push_back(i);
    	}
    }

    std::cout << p << " " << ans.size() << "\n";
    for (auto & x : ans) {
    	std::cout << x + 1 << " \n"[x == ans.back()];
    }
}

E. Thief in a Shop

题意:n个物品可以选无限个,你要选恰好k个物品,求所有组合中可以出现的所有总价值。

很显然的完全背包,可以记f[i][j]表示前i个物品凑成j价值最少需要几个物品。但发现我们求的是最小,如果j!=k我们不能判断是不是还可以选一些物品凑成k个,那么可以把所有物品都减去最小值,这之后每个价值需要凑成的物品数小于等于k个就可以拿若干个0价值的物品来凑。假设之前能被凑出k个的物品价值为m,因为每个物品的价值都减去了v,那么在变化后的dp方程里这个值是mk×v,那么这个值还是能被正确求出来,因为这只是所有物品和总价值的都减去了相同值,肯定还是能凑出来,那如果变化后的f[i][j]<k,一定能用0价值的物品凑成k个吗?这个时候价值其实j+f[i][j]×v,如果我们加上kf[i][j]v就能凑成k个,然后价值恰好是j+k×v

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    int min = *std::min_element(a.begin(), a.end());
    for (auto & x : a) {
    	x -= min;
    }

    const int N = 1e6 + 5, inf = 1e9;
    std::vector<int> f(N, inf);
    f[0] = 0;
    for (int i = 0; i < n; ++ i) {
    	for (int j = a[i]; j < N; ++ j) {
    		f[j] = std::min(f[j], f[j - a[i]] + 1);
    	}
    }

    for (int i = 0; i < N; ++ i) {
    	if (f[i] <= k) {
    		std::cout << i + k * min << " ";
    	}
    }
    std::cout << "\n";
}

F. Magic Matrix

题意:给你一个矩阵,判断是不是满足三个条件:

  1. 对于i[1,n],a[i][i]=0
  2. 对于i,j[1,n],a[i][j]=a[j][i]
  3. 对于i,j[1,n],a[i][j]<=mink=1nmax(a[i][k],a[j][k])

如果满足前两个条件,那么第三个条件可以变化为:a[i][j]<=mink=1nmax(a[i][k],a[k][j])==>a[i][j]<=mink=1nl=1nmax(a[i][k],a[k][l],a[l][j]),同理这三项还可以一直拆下去,最终是a[i][j]<=max(a[i][t1],a[i][t2],...,a[i][tm],a[i][j]),意味着对于任意一条ij的路径上的最大值都大于等于a[i][j]
那么我们可以类似kruskal,将边从小到大排序,然后对于边值相同的一块处理,如果这些边里有一条边连接的两个点在之前以及是在一个联通块里了,那么说明它们之间已经有路径了,且这个路径上所有边权都小于当前边边权,则输出NO,否则就把这些边都加进联通块里就行。可以用并查集维护联通块。

点击查看代码
struct DSU {
	std::vector<int> fa, cnt;
	DSU(int _n) {
		init(_n);
	}

	void init(int _n) {
		fa.assign(_n, 0);
		cnt.assign(_n, 1);
		std::iota(fa.begin(), fa.end(), 0);
	}

	int find(int x) {
		return x == fa[x] ? x : fa[x] = find(fa[x]);
	}

	bool merge(int x, int y) {
		x = find(x), y = find(y);
		if (x == y) {
			return false;
		}

		fa[y] = x;
		cnt[x] += cnt[y];
		return true;
	}

	bool same(int x, int y) {
		return find(x) == find(y);
	}

	int size(int x) {
		return cnt[find(x)];
	}
};

void solve() {
    int n;
    std::cin >> n;
    std::vector a(n, std::vector<int>(n));
    for (int i = 0; i < n; ++ i) {
    	for (int j = 0; j < n; ++ j) {
    		std::cin >> a[i][j];
    	}
    }

    for (int i = 0; i < n; ++ i) {
    	if (a[i][i] != 0) {
    		std::cout << "NOT MAGIC\n";
    		return;
    	}

    	for (int j = 0; j < n; ++ j) {
    		if (a[i][j] != a[j][i]) {
    			std::cout << "NOT MAGIC\n";
    			return;
    		}
    	}
    }

    std::vector<std::array<int, 3>> edges;
    for (int i = 0; i < n; ++ i) {
    	for (int j = 0; j < n; ++ j) {
    		if (i == j) {
    			continue;
    		}
    		edges.push_back({a[i][j], i, j});
    	}
    }

    std::sort(edges.begin(), edges.end());
    DSU dsu(n);
    for (int i = 0; i < edges.size(); ++ i) {
    	int j = i;
    	while (j < edges.size() && edges[j][0] == edges[i][0]) {
    		++ j;
    	}

    	-- j;

    	for (int k = i; k <= j; ++ k) {
    		if (dsu.same(edges[k][1], edges[k][2])) {
    			std::cout << "NOT MAGIC\n";
    			return;
    		}
    	}

    	for (int k = i; k <= j; ++ k) {
			dsu.merge(edges[k][1], edges[k][2]);
    	}

    	i = j;
    }

    std::cout << "MAGIC\n";
}
posted @   maburb  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示