Educational Codeforces Round 122 (Rated for Div. 2)/codeforces1633
CodeForces1633
Div. 7
解析:
题目大意
给定 \(t\) 组数据。每组数据给定一个数 \(n\)(\(10\le n\le 999\))。
每次操作可以修改 \(n\) 任意一位上的数,将这一位上的数修改为 \(0\sim 9\) 之间的任意数。要求使用最少的修改次数使这个数修改后是 \(7\) 的倍数,并且没有多余的前导 \(0\)。输出修改后的数,如果有多组解,输出任意一种即可。如果已经是 \(7\) 的倍数,那么不需要修改。
思路:
发现修改个位一定能让 \(n\) 能被 \(7\) 整除,直接枚举个位换哪个数。
code
#include <bits/stdc++.h>
using namespace std;
signed main()
{
int t; scanf ("%d", &t);
while (t--)
{
int n; scanf ("%d", &n);
if (n % 7 == 0) printf ("%d\n", n);
else
{
n = n / 10 * 10;
for (int i = n; i <= n + 10; i++)
if (i % 7 == 0) { printf ("%d\n", i); break; }
}
}
return 0;
}
Minority
解析:
题目大意:
- 给定一个 \(01\) 字符串 \(s\),定义 \(c_k(l,r)\) 表示 \(s\) 的由下标为 \([l,r]\) 中的字母构成的连续子串中 \(k\) 的个数。
- 定义 \(f(l,r)=\begin{cases}c_0(l,r)&c_0(l,r)<c_1(l,r)\\c_1(l,r)&c_0(l,r)>c_1(l,r)\\0&c_0(l,r)=c_1(l,r)\end{cases}\),求 \(\max\limits_{1\le l\le r\le n}f(l,r)\)。多组询问。
- \(T\le10^4,\sum|s|\le2\times10^5\)
思路:
诈骗题,\(f(1,n)\) 一定最大,因为这个区间含有的 \(0,1\) 都最多,如果 \(c_0(l,r)=c_1(l,r)\) 那个答案就是 \(c_0(l,r)-1\)(取 \([1,n-1]\))。
code:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
char ch[N];
int cnt1, cnt2;
signed main()
{
int t; scanf ("%d", &t);
while (t--)
{
cnt1 = cnt2 = 0;
scanf ("%s", ch + 1);
int n = strlen (ch + 1);
if (n <= 2) { printf ("0\n"); continue; }
for (int i = 1; i <= n; i++)
{
if (ch[i] == '0') cnt1++;
else cnt2++;
}
if (cnt1 != cnt2) printf ("%d\n", min(cnt1, cnt2));
else printf ("%d\n", cnt1 - 1);
}
return 0;
}
Kill the Monster
解析:
题目大意
在某款电脑游戏里,角色初始生命 \(hc\),初始攻击 \(dc\)。怪物初始生命 \(hm\),初始攻击 \(dm\)。
而你有 \(k\) 次机会,将攻击力永久提升 \(w\) 或者将生命永久提升 \(a\) 。
每一轮,角色攻击怪物,怪物掉 \(dc\) 生命。怪物攻击角色,角色掉 \(dm\) 生命。
问若干轮以后,角色能否击杀怪物并且角色存活。
击杀怪物定义为:怪物生命 \(\ngtr 0\)。
角色存活定义为:角色生命 \(>0\)。
思路:
枚举 \(0\to k\) 分别给攻击和生命多少次机会,如果某一时刻 $ \lceil\frac{hc}{dm}\rceil \ge \lceil\frac{hm}{dc}\rceil$ 那么就可以胜利。
code
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read ()
{
int x = 0, f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
return x * f;
}
signed main()
{
int t = read ();
while (t--)
{
int hc = read (), dc = read ();
int hm = read (), dm = read ();
int k = read (), w = read (), a = read ();
bool flag = false;
for (int i = 0; i <= k; i++)
{
int h = hc + (i * a), d = dc + ((k - i) * w);
if ((hm + d - 1) / d <= (h + dm - 1) / dm) { printf ("YES\n"); flag = true; break; }
}
if (!flag) printf ("NO\n");
}
return 0;
}
Make Them Equal
解析:
题目大意:
你有一个长度为 \(n\),初始全为 \(1\) 的数组 \(a\),和两个长度为 \(n\) 的数组 \(b,c\)。
你可以最多进行 \(k\) 次如下的操作:选择两个正整数 \(i,x\),使 \(a_{i}\) 变成 $ \left ( a_{i}+\left \lfloor \dfrac{a_{i}}{x} \right \rfloor \right )$。
最后,如果 \(a_{i}=b_{i}\),你将会获得 \(c_{i}\) 的收益。
最大化总收益。
- \(1 \leq t \leq 100,1 \leq n \leq 1000, 1 \leq k \leq 1 \times 10^{6}\)
- \(1 \leq b_{i} \leq 1000,1 \leq c_{i} \leq 1 \times 10^{6}\);
- \(\sum n \leq1000\)。
思路:
我们注意到 \(a_i\) 的初始值都是 \(1\),而 \(b_i\leq 1000\) 所以我们可以预处理出来每个值由 \(1\) 得到的最小次数,这部分预处理复杂度是 \(\mathcal O(V^2)\) 的。现在问题转化成了:
- 最多操作 \(k\) 次,还原第 \(i\) 个需要操作 \(cnt_{b_i}\) 次,可以得到 \(c_i\) 的价值,问最大价值。
发现问题变成了 01背包问题,复杂度 \(\mathcal O(nk)\),可能会被卡常,注意 \(k\geq \sum cnt_{b_i}\) 可以剪枝。
code:
#include <bits/stdc++.h>
using namespace std;
const int N = 1000 + 10;
const int K = 1e6 + 10;
typedef pair <int, int> pii;
inline int read ()
{
int x = 0, f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
return x * f;
}
int n, k;
int b[N], c[N], cnt[N * 2];
int dp[K];
void init ()
{
memset (cnt, 0x3f, sizeof (cnt));
cnt[1] = 0;
for (int i = 1; i <= 1000; i++)
for (int j = 1; j <= i; j++)
cnt[i + i / j] = min (cnt[i + i / j], cnt[i] + 1);
}
signed main()
{
int t = read ();
init ();
while (t--)
{
n = read (), k = read (); int sum = 0, s = 0;
for (int i = 1; i <= n; i++) b[i] = read (), sum += cnt[b[i]];
for (int i = 1; i <= n; i++) c[i] = read (), s += c[i];
if (k >= sum) { printf ("%d\n", s); continue; }
memset (dp, 0, sizeof (dp));
for (int i = 1; i <= n; i++)
for (int j = k; j >= cnt[b[i]]; j--)
dp[j] = max (dp[j], dp[j - cnt[b[i]]] + c[i]);
printf ("%d\n", dp[k]);
}
return 0;
}
Spanning Tree Queries
解析:
题目大意:
给定一个包含 \(n\) 个点和 \(m\) 条边的无向带权图,你有 \(k\) 次询问,第 \(i\) 次询问给定一个整数 \(q_i\),你需要从图中选出一棵生成树,设该生成树的 \(n-1\) 条边的权值为 \(w_1,w_2,\dots,w_{n-1}\),你需要求出 \(\sum\limits_{j=1}^{n-1}|w_j-q_i|\) 的最小值。
该题所有 \(k\) 次询问中,前 \(p\) 次询问的 \(q_1,q_2,\dots,q_p\) 在输入中给定,从第 \(p+1\) 次询问开始的 \(q_i=(q_{i-1}\cdot a+b)\bmod c\)。
- \(2\leqslant n\leqslant 50\),\(n-1\leqslant m\leqslant 300\),\(1\leqslant p\leqslant 10^5\),\(p\leqslant k\leqslant 10^7\),\(0\leqslant a,b\leqslant 10^8\),\(1\leqslant c\leqslant 10^8\)。
- \(0\leqslant w_i\leqslant 10^8\)。
思路:
发现这道题的 \(k\leq 10^7\),也就是我们必须在 \(\log\) 的时间内处理每个答案,这样 \(\mathcal O(km\log m)\) 的 \(k\) 次 Kruskal 肯定不行,我们需要考虑预处理答案。
而绝对值有好的性质,我们按照边权排序后,正常 Kruskal 是从头到尾扫,而绝对值则要求先扫在数轴上更靠近 \(q_i\) 的边,我们可以二分找到 \(q_i\) 在边权 \(e_i\) 中的具体位置,并将边权分为两类:小于 \(q_i\) 和大于 \(q_i\) 的。
假设 \(e_1\le e_2\le\cdots\le e_j\le q_i\le e_{j+1} \le \cdots \le e_m\),
则必有 \(|e_1-q_i| \ge |e_2-q_i| \ge \cdots \ge |e_j-q_i|\) 且 \(|e_{j+1}-q_i| \le|e_{j+2}-q_i| \le \cdots \le |e_m-q_i|\),我们可以开两个队列分别从 \(j\to 1\),\(j+1\to m\) 维护每条边权,每次取两个队列中与 \(q_i\) 绝对值的较小值,这样就达到了排序的目的。
同时,我们注意到 \(m\leq 300\),也就是我们可以对每个 \(j\) 分别考虑,考虑每个 \(e_j\leq q\leq e_{j+1}\) 时的生成树情况,但对于即使对于同一个 \(j\),不同的 \(q\) 所对应的生成树也是可能不同的,即在两个队列中会造成不同的选数顺序,我们考虑这样的情况最多有 \(\begin{pmatrix} m \\ 2 \\ \end{pmatrix}\) 种不同的可能(在 \(m\) 条边中任选两条边取平均值),这样最终不同生成树的个数是 \(\mathcal O(m)\cdot\mathcal O(m^2)=\mathcal O(m^3)\) 种。
时间复杂度 \(\mathcal O(m^3\log m+k\log m^2)\)。
code:
#include <bits/stdc++.h>
#define int long long
#define eb emplace_back
#define lob lower_bound
using namespace std;
const int N = 300 + 10;
const int SN = 1e5 + 10;
const int INF = 0x3f3f3f3f;
typedef pair <int, int> pii;
inline int read ()
{
int x = 0, f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
return x * f;
}
int n, m;
int p, k, a, b, c;
struct edge {
int u, v, w;
bool operator < (const edge &A)const {
return w < A.w;
}
}e[N];
int g[SN], cnt;
int ans[SN], cntA[SN][2];
queue <int> que[2];
struct DSU {
int fa[N];
void init () { for (int i = 1; i <= n; i++) fa[i] = i; }
inline int find (int x) { return x == fa[x] ? x : fa[x] = find (fa[x]); }
} d;
void clear (int k) { while (!que[k].empty ()) que[k].pop (); }
void kruskal (int id, int x)
{
d.init ();
clear (0); clear (1);
int pos = lob (e + 1, e + m + 1, (edge) {0, 0, x}) - e;
for (int i = pos - 1; i >= 1; i--) que[0].push (i);
for (int i = pos; i <= m; i++) que[1].push (i);
while (!que[0].empty () || !que[1].empty ())
{
int c, cid;
if (que[0].empty ()) c = 1, cid = que[1].front ();
else if (que[1].empty ()) c = 0, cid = que[0].front ();
else
{
if (abs (x - e[que[0].front ()].w) <= abs (x - e[que[1].front ()].w)) c = 0, cid = que[0].front ();
else c = 1, cid = que[1].front ();
}
int fx = d.find (e[cid].u), fy = d.find (e[cid].v);
if (fx != fy)
{
d.fa[fx] = fy;
ans[id] += abs (x - e[cid].w);
cntA[id][c]++;
}
que[c].pop ();
}
}
int Xor;
inline int getans (int q)
{
int x = lob (g + 1, g + cnt + 1, q) - g;
return ans[x] - cntA[x][0] * (g[x] - q) + cntA[x][1] * (g[x] - q);
}
signed main()
{
n = read (), m = read ();
for (int i = 1; i <= m; i++)
e[i].u = read (), e[i].v = read (), e[i].w = read ();
sort (e + 1, e + m + 1);
for (int i = 1; i <= m; i++)
for (int j = 1; j <= m; j++)
g[++cnt] = (e[i].w + e[j].w) >> 1;
g[++cnt] = INF;
sort (g + 1, g + cnt + 1);
cnt = unique (g + 1, g + cnt + 1) - g - 1;
for (int i = 1; i <= cnt; i++) kruskal (i, g[i]);
p = read (), k = read (), a = read (), b = read (), c = read ();
int q;
for (int i = 1; i <= p; i++) { q = read (); Xor ^= getans(q); }
for (int i = p + 1; i <= k; i++) { q = (q * a + b) % c; Xor ^= getans(q); }
printf ("%lld\n", Xor);
return 0;
}
6
解析:
题目大意:
思路:
没读题,先咕了
code: