Educational Codeforces Round 142 (A - F2)
赛时:A B C D
补题:A B C D E F1 F2
Rank: 1199
A
第一种操作只有对两个 作用才会更优。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <cstdlib>
#include <cmath>
#include <deque>
using namespace std;
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
const int N = 5e5 + 10;
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
int T, n;
int a[N];
int main()
{
T = read();
while (T--)
{
n = read();
for (int i = 1; i <= n; i++) a[i] = read();
sort(a + 1, a + n + 1);
int cnt = 0;
for (int i = 1; i <= n; i++)
{
if (a[i] > 1) break;
cnt++;
}
cnt = n - cnt + (cnt / 2) + (cnt % 2);
printf("%d\n", cnt);
}
return 0;
}
B
首先用光所有 ,然后交替使用 ,最后使用 。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <cstdlib>
#include <cmath>
#include <deque>
using namespace std;
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
const int N = 5e5 + 10;
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
int T, a, b, c, d, A, B, ans;
int main()
{
T = read();
while (T--)
{
a = read(), b = read(), c = read(), d = read();
ans = a;
A = a, B = a;
if (a == 0)
{
puts("1");
continue;
}
if (b >= c)
{
ans += c * 2;
b -= c;
if (B < b) ans += B + 1;
else
{
B -= b; ans += b;
ans += min(d, min(A, B) + 1);
}
}
else
{
ans += b * 2;
c -= b;
if (A < c) ans += A + 1;
else
{
A -= c; ans += c;
ans += min(d, min(A, B) + 1);
}
}
printf("%d\n", ans);
}
return 0;
}
C
设 为满足 的位置。
我们将 与 一起考虑,因为最优策略下,一定是 和 操作。
首先,如果 操作,那么 也得操作。
其次,如果 ,那么 需要操作。
最后,如果对于 , 或者 ,那么 需要操作。
将所有需要操作的数取最大值,就是答案。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <cstdlib>
#include <cmath>
#include <deque>
using namespace std;
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
const int N = 5e5 + 10;
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
int T, n, ans;
int p[N], pos[N];
int main()
{
T = read();
while (T--)
{
n = read();
for (int i = 1; i <= n; i++) p[i] = read(), pos[p[i]] = i;
ans = 0;
if (n == 1) {puts("0"); continue;}
if (n == 2)
{
if (pos[1] == 1) puts("0");
else puts("1");
continue;
}
int cur = 0;
for (int i = 1; i <= n / 2; i++)
if (pos[i] > pos[n - i + 1]) cur = i;
for (int i = 1; i <= (n + 1) / 2; i++)
if (pos[i] < pos[i - 1] || pos[n - i + 1] > pos[n - i + 2]) cur = max(cur, i - 1);
printf("%d\n", cur);
}
return 0;
}
D
对于一个 序列,我们要求出是否有 序列满足前 位都有 ,相当于要求 的第 位为 。
这是非常困难的,于是我们考虑反过来算。
已知一个 序列,它对哪些 如何贡献。
设 满足 ,那么相当于要求 。
于是我们枚举 属于 到 ,然后让前缀等于 的 的序列的答案对 取 。
简单哈希加 即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <cstdlib>
#include <cmath>
#include <deque>
using namespace std;
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
const int N = 5e4 + 10, M = 15;
typedef long long ll;
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
int T, n, m;
int a[N][M], p[M], pos[M];
map<ll, int> vis[M];
map<ll, int> ans[M];
ll count(int len)
{
ll res = 0;
for (int i = 1; i <= len; i++) res = (res * 10) + (ll)p[i] - 1ll;
return res;
}
int main()
{
T = read();
while (T--)
{
n = read(), m = read();
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++) a[i][j] = read(), p[j] = a[i][j];
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++) pos[a[i][j]] = j;
for (int res = 1; res <= m; res++)
{
for (int j = 1; j <= res; j++) p[j] = pos[j];
ans[res][count(res)]++;
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++) p[j] = a[i][j];
int res = 0;
for (int j = 1; j <= m; j++) if (ans[j][count(j)]) res = j;
printf("%d ", res);
}
puts("");
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++) pos[a[i][j]] = j;
for (int res = 1; res <= m; res++)
{
for (int j = 1; j <= res; j++) p[j] = pos[j];
ans[res][count(res)] = 0;
}
}
}
return 0;
}
E
结论: 的数最多有 个因子, 的数最多有 个因子。
因此我们首先暴力枚举出 的所有因子,然后直接做笛卡尔积求出 个可能的因子。
排序后去重,接下来考虑对于某一个因子如何算出它最早在第几行出现。
这里有一个复杂度奇奇怪怪的神秘做法。
对于每一个因子 ,它出现的行数一定是它自己的因子,因此也一定是 的因子。
于是我们二分找到最小的满足 的 ,然后从 开始向上枚举,直到 是 的因子或 。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <cstdlib>
#include <cmath>
#include <deque>
using namespace std;
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
const int N = 2e6 + 10;
#define int long long
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
int T, n, m1, m2, cnt1, cnt2, cnt3, ans, cnt;
int a[N], b[N], c[N];
signed main()
{
T = read();
while (T--)
{
n = read(), m1 = read(), m2 = read();
cnt1 = cnt2 = cnt3 = 0;
ans = cnt = 0;
for (int i = 1; i * i <= m1; i++)
{
if (m1 % i) continue;
a[++cnt1] = i;
if (i * i != m1) a[++cnt1] = m1 / i;
}
for (int i = 1; i * i <= m2; i++)
{
if (m2 % i) continue;
b[++cnt2] = i;
if (i * i != m2) b[++cnt2] = m2 / i;
}
sort(a + 1, a + cnt1 + 1);
sort(b + 1, b + cnt2 + 1);
for (int i = 1; i <= cnt1; i++)
for (int j = 1; j <= cnt2; j++) c[++cnt3] = a[i] * b[j];
sort(c + 1, c + cnt3 + 1);
cnt3 = unique(c + 1, c + cnt3 + 1) - c - 1;
for (int i = 1; i <= cnt3; i++)
{
int l = 1, r = i, mid, x = 0;
while (l <= r)
{
mid = (l + r) >> 1;
if ((c[i] + c[mid] - 1) / c[mid] <= n) r = mid - 1, x = mid;
else l = mid + 1;
}
int flag = 0;
for (int j = x; c[j] <= n; j++)
{
if (c[i] % c[j] == 0)
{
flag = 1;
ans ^= c[j];
cnt++;
break;
}
}
}
printf("%lld %lld\n", cnt, ans);
}
return 0;
}
F1
结论:一个子点集不可能既不蓝联通也不红联通。
证明:
因为整个图为完全图,所以一个子点集及其相关的边也是完全图。
容易发现,保留所有蓝边和保留所有红边互为补图,接下来只需要证明一个完全图自己或者其补图至少有一个联通。
假设在原图中有两个之间没有边的联通块。
那么在补图中这两个联通块之间的所有点对都有连边,也就一定联通。
有了这个结论,我们只需要计算不存在同时蓝联通和红联通的染色方案即可。
显然整个图作为一个子图也是蓝联通/红联通的。
设 表示 个点,整个图蓝联通的方案数, 表示 个点,整个图蓝联通/红联通的方案数。
如何转移?
枚举 号点所在的极大蓝联通块大小 ,那么这种方案对 的贡献是 。
因为这个联通块是极大的,所以这 个点到其余 的所有边都必须是红色的,因此此时算出的是对整个图红联通方案数的贡献。
把红色和蓝色互换后所有子图的联通颜色也互换,所以整个图红联通的方案数等于整个图蓝联通的方案数,这样贡献的是对的。
于是有转移方程:,。
容易发现第二条对 不成立, 特判即可。
答案是 。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <cstdlib>
#include <cmath>
#include <deque>
using namespace std;
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
const int N = 5e3 + 10, mod = 998244353;
#define int long long
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
int n;
int C[N][N], f[N], g[N];
signed main()
{
n = read();
C[0][0] = 1;
for (int i = 1; i <= n; i++)
{
C[i][0] = 1;
for (int j = 1; j <= i; j++) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
}
f[1] = 1, g[1] = 1;
for (int i = 2; i <= n; i++)
{
for (int j = 1; j < i; j++)
f[i] = (f[i] + f[j] * g[i - j] % mod * C[i - 1][j - 1] % mod) % mod;
g[i] = (f[i] * 2) % mod;
}
printf("%lld\n", (g[n] + mod - 2) % mod);
return 0;
}
F2
将 中的式子拆开:。这里的 对应 , 对应 。
。
设 。
。
这是一个非常漂亮的卷积形式,可惜 和 都对 有依赖性。
考虑根号重构,每 个元素计算一次 和 的卷积。
假设当前计算到 ,上一次卷积的编号在 ,考虑如何利用卷积结果快速计算 。
容易发现,对于 的所有 , 的和正好在卷积中计算过,答案记在 与 卷积得到的 数组第 项。
换言之,。
距离 中的求和式只差 和 的部分。
容易发现,这两部分的 最多只有 个,于是我们暴力计算这一部分的贡献即可。
。
复杂度为 。
当 时最优,为 。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <cstdlib>
#include <cmath>
#include <deque>
using namespace std;
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
const int N = 2e5 + 10, G = 3, GI = 332748118, mod = 998244353, T = 1000;
#define int long long
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
int n, t;
int qpow(int a, int b)
{
int res = 1;
while (b)
{
if (b & 1) res = (res * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return res;
}
void NTT(int len, int * a, int coe)
{
for (int mid = 1; mid < len; mid <<= 1)
{
int Wn = qpow(((coe == 1) ? G : GI), (mod - 1) / (mid << 1));
for (int i = 0; i < len; i += mid << 1)
{
int W = 1;
for (int j = 0; j < mid; j++, W = (W * Wn) % mod)
{
int x = a[i + j], y = (a[i + j + mid] * W) % mod;
a[i + j] = (x + y) % mod;
a[i + j + mid] = (x - y + mod) % mod;
}
}
}
}
int rev[N], c[N];
void mul(int len, int * a, int * b)
{
for (int i = 1; i < len; i++) rev[i] = (rev[i >> 1] >> 1) + ((i & 1) ? (len >> 1) : 0);
for (int i = 0; i < len; i++) if (i < rev[i]) swap(a[i], a[rev[i]]); NTT(len, a, 1);
for (int i = 0; i < len; i++) if (i < rev[i]) swap(b[i], b[rev[i]]); NTT(len, b, 1);
for (int i = 0; i <= len; i++) a[i] = (a[i] * b[i]) % mod;
for (int i = 0; i < len; i++) if (i < rev[i]) swap(a[i], a[rev[i]]); NTT(len, a, -1);
int inv = qpow(len, mod - 2);
for (int i = 0; i <= len; i++) c[i] = (a[i] * inv) % mod;
}
int len;
int A[N], B[N], C[N], D[N], a[N], b[N], inv[N], fac[N];
void init()
{
inv[0] = inv[1] = fac[0] = 1;
for (int i = 1; i <= n; i++) fac[i] = (fac[i - 1] * i) % mod;
for (int i = 2; i <= n; i++) inv[i] = (mod - mod / i) * inv[mod % i] % mod;
for (int i = 1; i <= n; i++) inv[i] = (inv[i - 1] * inv[i]) % mod;
}
signed main()
{
n = read(); init();
len = 1;
A[1] = B[1] = C[1] = D[1] = 1;
for (int i = 2; i <= n; i++)
{
if (t * 2 < i)
{
for (int j = 0; j <= i; j++) B[i] = (B[i] + C[i - j] * D[j] % mod) % mod;
B[i] = (B[i] * fac[i - 1]) % mod;
A[i] = (B[i] * 2) % mod;
C[i] = (A[i] * inv[i]) % mod;
D[i] = (B[i] * inv[i - 1]) % mod;
}
else
{
B[i] = c[i];
for (int j = 0; j < i - t; j++) B[i] = (B[i] + C[i - j] * D[j] % mod) % mod;
for (int j = t + 1; j <= i; j++) B[i] = (B[i] + C[i - j] * D[j] % mod) % mod;
B[i] = (B[i] * fac[i - 1]) % mod;
A[i] = (B[i] * 2) % mod;
C[i] = (A[i] * inv[i]) % mod;
D[i] = (B[i] * inv[i - 1]) % mod;
}
if (i - t == T)
{
while (len <= i * 2) len <<= 1;
for (int i = 0; i <= len; i++) a[i] = C[i], b[i] = D[i];
mul(len, a, b);
t = i;
}
}
printf("%lld\n", (A[n] + mod - 2) % mod);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构