我太神必了,B 炸了 次,C 炸了
次,DEF 一遍过。
不过第一次 AK 练习赛,还是比较激动的吧qwq。
A. CCA的数列
根据题意判断一下即可。
注意模和等比不能 的特判。
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5;
int n, a[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", a + i);
int b = a[2] - a[1];
bool ok = true;
for (int i = 3; i <= n; i++) {
if (a[i] - a[i - 1] != b) {
ok = false;
}
}
if (ok) {
puts("YES");
return 0;
}
if (a[1] != 0) {
ok = true;
for (int i = 3; i <= n; i++) {
if (a[i] == 0 || a[i - 1] == 0) {
ok = false;
break;
}
if ((LL)a[1] * a[i] != (LL)a[2] * a[i - 1]) {
ok = false;
break;
}
}
if (ok) {
puts("YES");
return 0;
}
}
if (a[1] != 0) {
ok = true;
int t = a[2] % a[1];
for (int i = 3; i <= n; i++) {
if (a[i - 1] == 0) {
ok = false;
break;
}
if ((LL)t != (LL)a[i] % a[i - 1]) {
ok = false;
break;
}
}
if (ok) {
puts("YES");
return 0;
}
}
puts("NO");
} B. CCA的字符串
考虑代表元计数法,把每个子串的贡献记录在最靠左的 NowCoder 上。
设 这一段是
NowCoder, 为上一个
NowCoder 左端点的位置,可取的左端点范围是 ,右端点是
,对答案的贡献即
。
时间复杂度线性(带个 8 的常数 /kk)
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5;
int n;
LL ans;
char a[N];
int main() {
scanf("%s", a + 1);
n = strlen(a + 1);
int last = 0;
for (int i = 1; i <= n; i++) {
if (i + 7 <= n && a[i] == 'N' && a[i + 1] == 'o' && a[i + 2] == 'w' && a[i + 3] == 'C' && a[i + 4] == 'o' && a[i + 5] == 'd' && a[i + 6] == 'e' && a[i + 7] == 'r') {
ans += (i - last) * (n - (i + 7) + 1ll);
last = i;
}
}
printf("%lld\n", ans);
} C. CCA的矩阵
本来想写个斜着的差分,但是 WA 了五次,后来发现中间有一段空缺不知道咋处理www。
后来换了一个写法,如果把这个网格斜着看,这个锤子的区域是 个长度为
的序列
个长度为
的序列,我想先把这个区域按照左上-右下这个线来划分,考虑记两个数组
分别代表向左上角延伸
、
个格子的权值和。
然后再设 为向右上延伸
个
的权值以及夹住的
个
的权值和。
以上所有东西都可以 一遍递推出来。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 2105;
typedef long long LL;
int n, K;
LL s[N][N], a[N][N], b[N][N], c[N][N];
LL ans;
int main() {
scanf("%d%d", &n, &K);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
scanf("%lld", &s[i][j]);
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
a[i][j] = s[i][j] + a[i - 1][j - 1];
if (i > K && j > K) a[i][j] -= s[i - K][j - K];
b[i][j] = s[i][j] + b[i - 1][j - 1];
if (i > K - 1 && j > K - 1) b[i][j] -= s[i - K + 1][j - K + 1];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
c[i][j] = c[i - 1][j + 1] + a[i][j] + b[i - 1][j] - a[i - K][j + K] - b[i - K][j + K - 1];
if (j + K - 1 <= n && j - K + 1 >= 1 && i - (2 * K - 1) + 1 >= 1) {
ans = max(ans, c[i][j]);
}
}
}
printf("%lld\n", ans);
} D. CCA的图
先找到 ,即按照标记从大到小排序加入边,使
变为联通的时刻对应的标记。
用并查集维护连通性即可。
然后找 方法同找
。
时间复杂度 。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 3e6 + 5, M = 3e6 + 5;
int n, m, s, t, L, R, f[N];
int find(int x) {
return x == f[x] ? x : f[x] = find(f[x]);
}
void inline merge(int x, int y) {
f[find(x)] = find(y);
}
struct E{
int a, b, c;
bool operator < (const E &b) const {
return c < b.c;
}
} e[M];
int main() {
scanf("%d%d%d%d", &n, &m, &s, &t);
for (int i = 1; i <= n; i++) f[i] = i;
for (int i = 1; i <= m; i++) scanf("%d%d%d", &e[i].a, &e[i].b, &e[i].c);
sort(e + 1, e + 1 + m);
int p;
for (int i = m; i; i --) {
merge(e[i].a, e[i].b);
if (find(s) == find(t)) {
p = i, L = e[i].c;
break;
}
}
while (p > 1 && e[p - 1].c == L) p--;
for (int i = 1; i <= n; i++) f[i] = i;
for (int i = p; i <= m; i++) {
merge(e[i].a, e[i].b);
if (find(s) == find(t)) {
R = e[i].c;
break;
}
}
printf("%d %d\n", L, R);
} E. CCA的期望
发现可以把黑色点个数期望拆成每个点的概率,每个点之间独立。
形式化的,设 为
编号的点经过
次成为黑色点的概率。
。
然后只要考虑算 就好了。
我们可以先枚举 ,如果原来就是黑点那么直接
就好了,白点的情况,设
为一次选择使
成为黑点的概率,这个可以在
的 Floyd 预处理后,用
的复杂度枚举两个选择的点
,
在一条
的最短路上即等价于
。
然后即求 次至少有一次选
的概率,其实可以考虑反向求没选的,然后减一下。因此就是
,加入答案即可。
考场 sb 了写了个简单的 DP + 矩阵快速幂优化。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 505, P = 1023694381;
int n, m, K, a[N], ans;
LL d[N][N];
int inline power(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = (LL)res * a % P;
a = (LL)a * a % P;
b >>= 1;
}
return res;
}
int main() {
memset(d, 0x3f, sizeof d);
scanf("%d%d%d", &n, &m, &K);
for (int i = 1; i <= n; i++) scanf("%d", a + i), d[i][i] = 0;
for (int i = 1; i <= m; i++) {
int u, v, w; scanf("%d%d%d", &u, &v, &w);
d[u][v] = d[v][u] = min(d[u][v], (LL)w);
}
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
for (int i = 1; i <= n; i++) {
if (a[i] == 1) {
(ans += 1) %= P;
continue;
}
int s = 0, cur = 0;
for (int u = 1; u <= n; u++) {
for (int v = 1; v <= n; v++) {
if (u == v) continue;
s++;
if (d[u][v] == d[u][i] + d[i][v]) cur++;
}
}
int p = (LL)(s - cur) * power(s, P - 2) % P;
ans = (ans + 1ll + P - power(p, K)) % P;
}
printf("%d\n", ans);
} F. CCA的树
Cayley 定理: 个点的有标号无根树形态有
种。
然后那个分母就有了(然而我做到最后发现根本不用知道这个东西,因为可以消掉)。
考虑算所有形态染色方案数之和。
个好链这种不好求,转化成总数
没有好链的。
总数即随便染色 。
考虑何种情况下没有好链,发现没有好链等价于在这颗树中,要么每种颜色的出现次数 ,要么所有颜色相同的点都仅仅的靠拢在一起,即每个颜色聚拢成一个联通块。
因此对于每种形态的树而言,我们可以枚举断开的边数 ,选
条边
。考虑给
个联通块染色
。 注意这里的联通块,边本身没有编号的含义,但我们为了区分他,可以人为随便进行编号。因此贡献是:$
$
通过上段,我们发现每种形态对应方案数(总数和不合法数)是一样的,这个期望是唬我们的。
我们可以 预处理阶乘及其逆元,
枚举断边。
因此总复杂度 。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 1e7 + 5, P = 1023694381;
int n, m, L, fact[N], infact[N];
int inline power(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = (LL)res * a % P;
a = (LL)a * a % P;
b >>= 1;
}
return res;
}
int inline C(int a, int b) {
return (LL)fact[a] * infact[b] % P * infact[a - b] % P;
}
int main() {
scanf("%d%d", &n, &m); L = max(n, m);
fact[0] = infact[0] = 1;
for (int i = 1; i <= L; i++) fact[i] = (LL)fact[i - 1] * i % P;
infact[L] = power(fact[L], P - 2);
for (int i = L - 1; i; i--) infact[i] = (LL)infact[i + 1] * (i + 1) % P;
int x = power(m, n) % P;
for (int i = 0; i <= n - 1; i++) {
if (i + 1 > m) continue;
x = ((LL)x + P - (LL)C(n - 1, i) * C(m, i + 1) % P * fact[i + 1] % P) % P;
}
printf("%d\n", x);
} 
京公网安备 11010502036488号