题意:
给我们\(x\)张赞成票和\(y\)张否决票还有\(z\)张不确定的票,让我们判断最后的选举结果是什么,如果是赞成的是"+", 否决的是"-", 不确定的是"?", 平票的是"0"
思路:
分类讨论一下就行了
int x, y, z; std::cin >> x >> y >> z; if (x > y + z) std::cout << "+\n"; else if (x + z < y) std::cout << "-\n"; else if (x == y && !z) std::cout << "0\n"; else if (x + z >= y || y + z <= x) std::cout << "?\n";
题意:
给我们\(n\)个数,这\(n\)个数的范围是\(1\leq x \leq n\),且这\(n\)个数互不相同。要求我们构造一个\(m * m\)的网格,将这\(n\)个数放进去,且满足\(|r_i - r_j| + |c_i - c_j| \geq |i - j|\)。第一行输出\(m\),接下来的\(m\)行输出\(n\)个点的坐标。
思路:
要让这个网格尽可能的小,也就是要让这个网格满足\(m - 1 + m - 1 \geq n - 1\)。那么根据这个表达式我们可以推出在满足刚好把\(n\)个数放下的最小的\(m\)值。
\(m = \frac{n - (n \% 2 != 0)}{2} + 1\)。接下来我们就只需要思考怎么构造了。我们先把\(1\)放在网格的左上角即\((1,1)\), \(n\)放在\((m,m)\),然后我们就只需要在第一行和最后一行放数就可以了。我们知道\((1,1)\)和\((m,m)\)之间的曼哈顿距离为\(m - 1 + m - 1\),且\(m - 1 + m - 1 \geq n - 1\) 所以我们把\(n - 1\)放在\((m, m - 1)\)的位置一定也满足这个条件。
void solve() { int n; std::cin >> n; int m = (n - n % 2) / 2 + 1; std::cout << m << "\n"; std::vector<std::pair<int, int>> v(n + 1); int idx = 0; for (int i = n; i > n - m; i --, idx ++ ) v[i] = {m, m - idx}; for (int i = 1; i <= n - m; i ++ ) v[i] = {1, i}; for (int i = 1; i <= n; i ++ ) std::cout << v[i].first << " " << v[i].second << "\n"; }
题意:
有两堆牌每一堆牌都有\(n\)张且有\(n\)张是\(0\),另外\(n\)张是\(1\sim n\)的排列,自己手中的牌可以按照自己的意愿打出,并放入另外一堆的牌底。每一次都可以从牌堆顶获得一张牌。要我们求出使得牌堆内的元素从牌堆顶到牌堆底是单调递增的操作次数。
思路:
因为我们要让不在手中的牌是\(n\)张牌\(1\sim n\)的单调递增的排列方式,所以我们首先要找到\(1\)的位置。如果\(1\)是在自己的手中的话,我们就要求出使得我们在牌堆底不断加数得时候能够让第\(x\)张能够被获得且打出,所以最后得答案就是\(\max\limits_{0\leq i \leq n - 1}\{i-b_i + 2\} + n\)。如果\(1\)是在牌堆中,并且在\(1\)后面得数是公差为\(1\)得等差数列,并且前面的非\(0\)数可以在轮到它插入牌堆底之前或那一刻出队,答案就是\(pos_1\)。否则的话答案就是\(\max\limits_{0\leq i \leq n - 1}\{i-b_i + 2\} + n\)。
void solve() { int n; std::cin >> n; std::vector<int> a(n), b(n); int pos = -1; for (auto& I : a) std::cin >> I; rep(i, 0, n) { std::cin >> b[i]; if (b[i] == 1) pos = i; } if (~pos) { bool f = true; rep(i, pos + 1, n) if (b[i] != b[i - 1] + 1) { f = false; break; } // 判断1后面的数是不是连续的 rep(i, 0, pos) if (b[i] && b[i] - i - 1 <= n - pos) { f = false; break; } // b[i] - i - 1表示的是这个数到出队的时候能不能接在当时的后面 if (f) {std::cout << pos << "\n"; return ;} // 如果全部都满足的话就只需要操作pos次 } int ans = 0; rep(i, 0, n) if (b[i]) ans = std::max(ans, i - b[i] + 2); std::cout << ans + n << "\n"; // 如果上面的要求都不满足的话队列一定要全部清空一遍所以+n }
题意:
给定一棵\(n\)个节点的树,从\(1\)到\(n\)编号,现在你需要玩弄这棵树。问按照顺时针遍历能获得多少种不同的序列。最后的答案对\(\%998244353\)
思路:
定义\(son[u]\)表示\(u\)的子节点的个数。先固定\(1\)是这个序列中的第一个,因为这是一个环所以最后的答案要乘上\(n\)。定义\(dp[u]\)表示的是以\(u\)为节点的方案数。
对于节点\(1\),我们可以就是对\(son[u]\)进行全排列,并将排列方式放在\(1\)的后面,将\(son[u]\)全排列一共有\(A_{son[u]} ^ {son[u]}\)种方案,也就是\(son[u]!\)种方案,而它的每一个儿子\(v\)同样也有它们的子节点,所以就得到了式子$$dp[u] = son[u]! * \prod_{v\in G[u]}{dp[v]}$$
而对于非\(1\)节点,它们自身也可以进行全排列所以对于非\(1\)的节点,它们的方案数的表达式是\(dp[u] = (son[u] + 1)! * \prod_{v\in G[u]}{dp[v]}\)
所以最后我们将两种情况合并就是$$dp[u] = du[u]! \prod_{v\in G[u]}{dp[v]}$$其中\(du[u]\)表示的是\(du[u]\)的度
void solve() { int n; std::cin >> n; fac[0] = 1; rep(i,1,N) fac[i] = fac[i - 1] * i; std::vector<int> G[n + 1], du(n + 1); rep(i,0,n - 1) { int u, v; std::cin >> u >> v; G[u].push_back(v), G[v].push_back(u); du[v] ++, du[u] ++; } std::vector<Z> dp(n + 1); std::function<void(int, int)> dfs = [&] (int u, int fa) -> void { Z res = 1; for (auto v : G[u]) { if (v == fa) continue; dfs(v, u); res = res * dp[v]; } dp[u] = fac[du[u]] * res; }; dfs(1, 0); Z ans = dp[1] * n; std::cout << ans.val() << "\n"; }