Cnoi2020
整体感受
先来说一下我的整体感受。
首先这个A的难度还是可以的,毕竟A题本来的定位就是送分,而且也不是纯模拟枚举就可以过的无脑题。不过提高组以上的选手大概都可以1min切?
B的话虽然水但也不是那么模板吧,反正B本来也是送分的。
这个C、D都是概率期望对数学不好的一点都不友好,而且被喷板子。
总体来说还是可以的吧,考察也还算全面。因为中间被拉去搞whk所以只切了AB和C的40分/kk。
Solution
[Cnoi2020]子弦
出现次数最多的非空子串所包含的每个单独的字符也都跟着出现了,所以长度为1的串出现次数只多不少,所以只考虑长度为1的串就行了。
然而其实这题还是后缀自动机的板子。
namespace Solve{
static int cnt[26], ans;
static char s[10000010];
void MAIN() {
scanf("%s", s + 1);
int len = strlen(s + 1);
for (int i = 1; i <= len; i++) cnt[s[i] - 'a']++;
for (int i = 0; i < 26; i++) ans = max(ans, cnt[i]);
StandardIO :: print(ans);
}
}
[Cnoi2020]雷雨
一定是先分别到一个点然后剩下的路一起走,所以枚举那个点,然后算3个最短路就好了。(也被喷最短路板子)
namespace Solve{
const int MAXN = 1010;
const long long inf = 0x3f3f3f3f3f3f3f3f;
struct node{
int x, y;
long long dis;
friend bool operator < (node pp, node qq) {
return pp.dis > qq.dis;
}
};
priority_queue<node> q;
static int n, m, a, b, c;
static int dx[4] = {0, 0, 1, -1};
static int dy[4] = {1, -1, 0, 0};
static long long dis[3][MAXN][MAXN], mp[MAXN][MAXN], ans;
bool vis[MAXN][MAXN];
void calc(int id, int sx, int sy) {
memset(vis, false, sizeof(vis));
dis[id][sx][sy] = mp[sx][sy];
while (!q.empty()) q.pop();
q.push(node{sx, sy, dis[id][sx][sy]});
while (!q.empty()) {
node cur = q.top();
q.pop();
if (vis[cur.x][cur.y]) continue;
vis[cur.x][cur.y] = true;
for (int i = 0; i < 4; i++) {
node nxt = node{cur.x + dx[i], cur.y + dy[i], cur.dis + mp[cur.x + dx[i]][cur.y + dy[i]]};
if (dis[id][nxt.x][nxt.y] > nxt.dis) {
dis[id][nxt.x][nxt.y] = nxt.dis;
q.push(nxt);
}
}
}
}
void MAIN() {
StandardIO :: read(n); StandardIO :: read(m);
StandardIO :: read(a); StandardIO :: read(b); StandardIO :: read(c);
for (int i = n; i >= 1; i--) {
for (int j = 1; j <= m; j++) {
StandardIO :: read(mp[i][j]);
dis[0][i][j] = dis[1][i][j] = dis[2][i][j] = inf;
}
}
calc(0, n, a);
calc(1, 1, b);
calc(2, 1, c);
ans = inf;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
ans = min(ans, dis[0][i][j] + dis[1][i][j] + dis[2][i][j] - 2 * mp[i][j]);
}
}
StandardIO :: print(ans);
}
}
[Cnoi2020]梦原
考虑一个点 \(v\) 接在另一个点 \(u\) 的后面,如果 \(a_u > a_v\) 则在消 \(u\) 的过程中就可以把 \(v\) 消掉了,所以只考虑 \(a_u<a_v\)。
这种情况的贡献是 \(a_v-a_u\),类似的题:积木大赛。再乘上 \(v\) 连边的概率就好了。
实现考虑树状数组。
namespace Solve{
const long long mod = 998244353;
const int MAXN = 1000010;
static int n, nn, k, h[MAXN], g[MAXN];
static long long ans;
static long long sum[MAXN], num[MAXN];
int lowbit(int x) { return x & -x; }
void Sub(int x, long long v) { while (x <= nn) sum[x] = (sum[x] - v + mod) % mod, num[x]--, x += lowbit(x); }
void Add(int x, long long v) {while (x <= nn) sum[x] = (sum[x] + v) % mod, num[x]++, x += lowbit(x); }
long long Ask_Sum(int x) {
long long ret = 0;
while (x) ret = (ret + sum[x]) % mod, x -= lowbit(x);
return ret;
}
long long Ask_Num(int x) {
long long ret = 0;
while (x) ret = (ret + num[x]) % mod, x -= lowbit(x);
return ret;
}
long long ksm(long long a, int b) {
long long ret = 1;
while (b) {
if (b & 1) ret = (ret * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return ret;
}
void MAIN() {
StandardIO :: read(n); StandardIO :: read(k);
for (int i = 1; i <= n; i++) StandardIO :: read(h[i]), g[i] = h[i];
sort(g + 1, g + 1 + n);
nn = unique(g + 1, g + 1 + n) - g - 1;
for (int i = 1; i <= n; i++) h[i] = lower_bound(g + 1, g + 1 + nn, h[i]) - g;
ans = g[h[1]];
Add(h[1], g[h[1]]);
for (int i = 2; i <= n; i++) {
int fir = max(1, i - k);
if (fir > 1) Sub(h[fir - 1], g[h[fir - 1]]);
ans = (ans + (g[h[i]] * Ask_Num(h[i]) % mod - Ask_Sum(h[i]) + mod) % mod * ksm(min(i - 1, k), mod - 2) % mod) % mod;
Add(h[i], g[h[i]]);
}
StandardIO :: print(ans);
}
}
[Cnoi2020]线形生物
近乎图上随机游走的板子。
解法一
先设每条边的出度为 \(d_i\),从 \(i\) 走到 \(n+1\) 期望走 \(f_i\) 步。则有 \(f_i=\frac{1}{d_i}\times\sum_{i\rightarrow j}f_j+1\)。
分析可得:\(f_i=\frac{1}{d_i}\times(f_{i+1}+\sum_{i\rightarrow j,j\neq i+1}f_j)+1\Rightarrow f_{i+1}=d_i\times(f_i-1)-\sum_{i\rightarrow j, j \neq i+1}f_j\)。
由此我们可以知道 \(f_i\) 一定是关于 \(f_1\) 的线性函数,即 \(f_i=A_if_1+B_i\)。显然 \(A_1=1,B_1=0\)。
而 \(f_{n+1}=A_{n+1}f_1+B{n+1}=0 \Rightarrow f_1=-\frac{B_{n+1}}{A_{n+1}}\)。
所以我们现在要求 \(A,B\)。根据刚刚的递推式可以得出:
\(A_{i+1}f_1+B_{i+1}=d_i(A_if_1+B_i-1)-\sum_{i\rightarrow j}(A_jf_1+B_j)\).
\(A_{i+1}=d_iA_i-\sum_{i\rightarrow j}A_j\).
\(B_{i+1}=d_i(B_i-1)-\sum_{i\rightarrow j}B_j\).
然后由于返祖边只有 \(m\) 条,所以可以在 \(O(n+m)\) 的时间内解决。
namespace Solve{
const long long mod = 998244353;
const int MAXN = 2000010;
static int id, n, m;
static long long d[MAXN], a[MAXN], b[MAXN];
vector<int> vec[MAXN];
long long ksm(long long x, long long y) {
long long ret = 1;
while (y) {
if (y & 1) ret = (ret * x) % mod;
x = (x * x) % mod;
y >>= 1;
}
return ret;
}
void MAIN() {
read(id); read(n); read(m);
for (int i = 1, u, v; i <= m; i++) {
read(u); read(v);
vec[u].push_back(v);
d[u]++;
}
for (int i = 1; i <= n; i++) d[i]++;
a[1] = 1, b[1] = 0;
for (int i = 1; i <= n; i++) {
a[i + 1] = d[i] * a[i] % mod;
b[i + 1] = d[i] * (b[i] - 1) % mod;
for (int j = 0; j < (int)vec[i].size(); j++) {
a[i + 1] = ((a[i + 1] - a[vec[i][j]]) % mod + mod) % mod;
b[i + 1] = ((b[i + 1] - b[vec[i][j]]) % mod + mod) % mod;
}
}
print(((-b[n + 1] % mod + mod) % mod * ksm(a[n + 1], mod - 2) % mod + mod) % mod);
}
}
解法二
定义 \(f_i\) 代表从 \(i\) 到 \(i+1\) 的期望步数。
则有 \(f_i=\frac{1}{d_i}(1+\sum_{i\rightarrow j}sum_i-sum_{j-1})\)。其中 \(sum_i\) 是 \(f\) 的前缀和。再把 \(f_i\) 从 \(sum_i\) 分离出来也可以做。
最后答案是 \(\sum_{i=1}^{n}f_i\)