整体感受
先来说一下我的整体感受。
首先这个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} imessum_{i ightarrow j}f_j+1)。
分析可得:(f_i=frac{1}{d_i} imes(f_{i+1}+sum_{i ightarrow j,j eq i+1}f_j)+1Rightarrow f_{i+1}=d_i imes(f_i-1)-sum_{i ightarrow j, j eq 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 ightarrow j}(A_jf_1+B_j)).
(A_{i+1}=d_iA_i-sum_{i ightarrow j}A_j).
(B_{i+1}=d_i(B_i-1)-sum_{i ightarrow 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 ightarrow j}sum_i-sum_{j-1}))。其中 (sum_i) 是 (f) 的前缀和。再把 (f_i) 从 (sum_i) 分离出来也可以做。
最后答案是 (sum_{i=1}^{n}f_i)