Codeforces Round 972 (Div. 2)
A. Simple Palindrome
给定整数 ,构造长度为 的只由a
e
i
o
u
的字符串,使得它的回文子序列最少。
容易发现 aia
不如 aai
优,贪心的将每种字符放在一起,并将总个数尽量均分到每个字符上。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
int read()
{
int x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
void solve()
{
int n = read();
int k = n / 5, res = n % 5;
for(int i = 1; i <= k; ++i) printf("a");
if(res) {printf("a"); --res;}
for(int i = 1; i <= k; ++i) printf("e");
if(res) {printf("e"); --res;}
for(int i = 1; i <= k; ++i) printf("i");
if(res) {printf("i"); --res;}
for(int i = 1; i <= k; ++i) printf("o");
if(res) {printf("o"); --res;}
for(int i = 1; i <= k; ++i) printf("u");
printf("\n");
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
B1. The Strict Teacher (Easy Version)
有一排 间房间, 个老师,一个学生在逃课,最初所有人的位置互不相同,每一步每个人都可以留在原地或者向相邻的房间走,问最多需要几步抓住学生。
如果没有老师在学生的左边或者右边,那么学生可以跑到最左边或者最右边直到被抓住。
如果学生两边都有老师,设最近的两个老师中间的房子为 ,每次都可以使 ,直到 小于等于 。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
int read()
{
int x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 2e5 + 5;
int a[N];
void solve()
{
int n = read(), m = read(), q = read();
for(int i = 1; i <= m; ++i) a[i] = read();
sort(a + 1, a + m + 1);
while(q--)
{
int pos = read();
if(pos < a[1]) printf("%d\n", a[1] - 1);
else if(pos > a[m]) printf("%d\n", n - a[m]);
else
{
int id = upper_bound(a + 1, a + m + 1, pos) - a;
int len = a[id] - a[id - 1] - 1;
printf("%d\n", (len + 1) / 2);
}
}
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
B2. The Strict Teacher (Hard Version)
同 。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
int read()
{
int x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 2e5 + 5;
int a[N];
void solve()
{
int n = read(), m = read(), q = read();
for(int i = 1; i <= m; ++i) a[i] = read();
sort(a + 1, a + m + 1);
while(q--)
{
int pos = read();
if(pos < a[1]) printf("%d\n", a[1] - 1);
else if(pos > a[m]) printf("%d\n", n - a[m]);
else
{
int id = upper_bound(a + 1, a + m + 1, pos) - a;
int len = a[id] - a[id - 1] - 1;
printf("%d\n", (len + 1) / 2);
}
}
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
C. Lazy Narek
给定 个长度为 的字符串,一个人 和 玩游戏,对于一个字符串 ,
的策略是循环选取n
a
r
e
k
,每选出一组,,不足一组不算 选取的。
对于 没有选取的n
a
r
e
k
,每一个字符使 。
目标是,在不改变 的字符串的相对顺序的情况下,选取一些字符串依次连接构成 ,使 最大。
容易发现秉持能匹配则匹配的原则一定最优。
发现只有5种字符有用,且字符串顺序不变,设 表示考虑前 个串,最后一组匹配了 个字符时, 最大。
是最后 个字符既不算入 的分数,也不算入 的分数。
首先对第 个字符串 求出 ,表示前 个字符串最后一组匹配了 个字符时,匹配完 后最后一组匹配了 个字符,。
转移时枚举前 个字符串最后一组的匹配字符数。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
int read()
{
int x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int inf = 0x3f3f3f3f;
const int N = 1e3 + 5;
int n, m;
int id[500], vis[500];
char s[N];
int sum[5], len[5];
void init(char s[])
{
for(int i = 0; i <= 4; ++i) sum[i] = 0, len[i] = i;
for(int i = 1; i <= m; ++i)
{
if(vis[s[i]])
{
for(int j = 0; j <= 4; ++j)
{
if(id[s[i]] == len[j] + 1)
{
len[j]++;
if(len[j] == 5) sum[j] += 5, len[j] = 0;
}else sum[j]--;
}
}
}
}
int dp[N][6];
void solve()
{
n = read(), m = read();
for(int i = 0; i <= n; ++i)
for(int j = 0; j <= 5; ++j)
dp[i][j] = -inf;
dp[0][0] = 0;
for(int i = 1; i <= n; ++i)
{
scanf("%s", s + 1);
init(s);
// for(int j = 0; j <= 4; ++j) printf("sum = %d len = %d\n", sum[j], len[j]);
// printf("\n");
for(int j = 0; j <= 4; ++j) dp[i][j] = dp[i - 1][j];
for(int j = 0; j <= 4; ++j)
dp[i][len[j]] = max(dp[i][len[j]], dp[i - 1][j] + sum[j]);
}
int ans = 0;
for(int i = 0; i <= 4; ++i) ans = max(ans, dp[n][i] - i);
printf("%d\n", ans);
}
int main()
{
id['n'] = 1, id['a'] = 2, id['r'] = 3, id['e'] = 4, id['k'] = 5;
vis['n'] = 1, vis['a'] = 1, vis['r'] = 1, vis['e'] = 1, vis['k'] = 1;
int T = read();
while(T--) solve();
return 0;
}
D. Alter the GCD
注意到一个序列的前缀 最多变化 次。
记选择翻转区间的 为 和 ,同理设前后缀 为 。
考虑枚举选择翻转的段的左端点 ,对于不同的 , 都最多变化 次。
那么对于不同的 ,至多有 的位置会改变四种 ,每次二分找到下一次改变 的位置即可。
对于答案统计,由于每一段左端点固定,右端点是一段区间且四种 都不变,方案数即右端点所在区间长度。
二分+求+右端点次变化 = 。
(这能过5e5?)
考虑如何优化,发现瓶颈在于每次需要二分找到四种 变化的位置,由于是每次删去一个数使得无法维护 ,考虑倒序枚举左端点,每次增加一个数,可以从上一个左端点转移过来。
具体的,当左端点从 到 时,对于所有的中间区间 需要再和 做 。由于这种 最多有 个,所有每次可以 维护。
对于所有的后缀区间 只需要再加入一个 。每次 维护。
总复杂度 。
实现上,需要将四种 取出来排序,为了方便使用了 函数,实际复杂度应为
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
int read()
{
int x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 2e5 + 5;
int n, a[N], b[N];
int lg[N], stA[20][N], stB[20][N];
int gcd(int x, int y)
{
if(!x || !y) return x | y;
return gcd(y, x % y);
}
int getgcd(int l, int r, int d)
{
if(l > r) return 0;
int k = lg[r - l + 1];
if(d == 0) return gcd(stA[k][l], stA[k][r - (1 << k) + 1]);
if(d == 1) return gcd(stB[k][l], stB[k][r - (1 << k) + 1]);
return 0;
}
void solve()
{
n = read();
for(int i = 1; i <= n; ++i) a[i] = read();
for(int i = 1; i <= n; ++i) b[i] = read();
for(int i = 1; i <= n; ++i) stA[0][i] = a[i], stB[0][i] = b[i];
for(int i = 1; i <= 19; ++i)
for(int j = 1; j + (1 << i) - 1 <= n; ++j)
{
stA[i][j] = gcd(stA[i - 1][j], stA[i - 1][j + (1 << (i - 1))]);
stB[i][j] = gcd(stB[i - 1][j], stB[i - 1][j + (1 << (i - 1))]);
}
ll ans = 0, sum = 0;
int preA = 0, preB = 0;
for(int l = 1; l <= n; ++l)
{
int midA = a[l], sufA = getgcd(l + 1, n, 0), midB = b[l], sufB = getgcd(l + 1, n, 1);
int last = l, L = l, R = n;
while(L <= n)
{
while(L < R)
{
int mid = (L + R + 1) >> 1;
if(midA == getgcd(l, mid, 0) && sufA == getgcd(mid + 1, n, 0) && midB == getgcd(l, mid, 1) && sufB == getgcd(mid + 1, n, 1)) L = mid;
else R = mid - 1;
}
int tmp = gcd(preA, gcd(midB, sufA)) + gcd(preB, gcd(midA, sufB)), cnt = L - last + 1;
// printf("l = %d, r = %d, preA = %d, midA = %d, sufA = %d, preB = %d, midB = %d, sufB = %d\n", l, L, preA, midA, sufA, preB, midB, sufB);
// printf("tmp = %d, sum = %d\n", tmp, sum);
if(tmp > ans) ans = tmp, sum = cnt;
else if(tmp == ans) sum += cnt;
++L, R = n, last = L;
midA = getgcd(l, L, 0), sufA = getgcd(L + 1, n, 0), midB = getgcd(l, L, 1), sufB = getgcd(L + 1, n, 1);
}
preA = gcd(preA, a[l]), preB = gcd(preB, b[l]);
}
printf("%lld %lld\n", ans, sum);
}
int main()
{
lg[0] = -1;
for(int i = 1; i <= 200000; ++i) lg[i] = lg[i >> 1] + 1;
int T = read();
while(T--) solve();
return 0;
}
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
int read()
{
int x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 2e5 + 5;
int n, a[N], b[N];
int preA[N], preB[N], SUFA[N], SUFB[N];
vector< pair<int, int> > sufA, sufB, midA, midB;
vector< pair<int, int> > tmp;
int lg[N], stA[20][N], stB[20][N];
int gcd(int x, int y)
{
if(!y) return x;
return gcd(y, x % y);
}
void init()
{
for(int i = 1; i <= n; ++i) stA[0][i] = a[i], stB[0][i] = b[i];
for(int i = 1; i <= 19; ++i)
for(int j = 1; j + (1 << i) - 1 <= n; ++j)
stA[i][j] = gcd(stA[i - 1][j], stA[i - 1][j + (1 << (i - 1))]),
stB[i][j] = gcd(stB[i - 1][j], stB[i - 1][j + (1 << (i - 1))]);
}
int getgcd(int l, int r, int d)
{
if(l > r) return 0;
int k = lg[r - l + 1];
if(d == 0) return gcd(stA[k][l], stA[k][r - (1 << k) + 1]);
else return gcd(stB[k][l], stB[k][r - (1 << k) + 1]);
}
int sta[N], top, vis[N];
void solve()
{
n = read();
for(int i = 1; i <= n; ++i) a[i] = read(), preA[i] = gcd(a[i], preA[i - 1]);
for(int i = 1; i <= n; ++i) b[i] = read(), preB[i] = gcd(b[i], preB[i - 1]);
SUFA[n + 1] = SUFB[n + 1] = 0;
for(int i = n; i >= 1; --i) SUFA[i] = gcd(a[i], SUFA[i + 1]), SUFB[i] = gcd(b[i], SUFB[i + 1]);
init();
sufA.clear(), sufB.clear(), midA.clear(), midB.clear();
int ans = 0;
ll sum = 0;
sufA.emplace_back(pair<int, int>(a[n], n));
sufB.emplace_back(pair<int, int>(b[n], n));
midA.emplace_back(pair<int, int>(a[n], n));
midB.emplace_back(pair<int, int>(b[n], n));
for(int l = n - 1; l >= 0; --l)
{
for(auto [val, id] : sufA) if(!vis[id - 1]) sta[++top] = id - 1, vis[id - 1] = 1;
for(auto [val, id] : sufB) if(!vis[id - 1]) sta[++top] = id - 1, vis[id - 1] = 1;
for(auto [val, id] : midA) if(!vis[id]) sta[++top] = id, vis[id] = 1;
for(auto [val, id] : midB) if(!vis[id]) sta[++top] = id, vis[id] = 1;
sort(sta + 1, sta + top + 1);
if(!vis[n]) sta[++top] = n, vis[n] = 1;
sta[++top] = n + 1;
for(int i = 1; i < top; ++i)
{
int r = sta[i];
if(r <= l) continue;
int MIDA = getgcd(l + 1, r, 0), MIDB = getgcd(l + 1, r, 1);
int ansA = gcd(preA[l], gcd(MIDB, SUFA[r + 1])), ansB = gcd(preB[l], gcd(MIDA, SUFB[r + 1]));
if(ansA + ansB > ans){ ans = ansA + ansB, sum = sta[i + 1] - sta[i]; }
else if(ansA + ansB == ans) sum += sta[i + 1] - sta[i];
}
for(int i = 1; i < top; ++i) vis[sta[i]] = 0;
top = 0;
if(!l) continue;
pair<int, int> flag1 = *--sufA.end();
int flag2 = gcd(flag1.first , a[l]);
if(flag1.first == flag2) sufA.pop_back();
sufA.emplace_back(pair<int, int>(flag2, l));
flag1 = *--sufB.end();
flag2 = gcd(flag1.first , b[l]);
if(flag1.first == flag2) sufB.pop_back();
sufB.emplace_back(pair<int, int>(flag2, l));
tmp.emplace_back(pair<int, int>(a[l], l));
for(auto [val, id] : midA)
{
int newval = gcd(val, a[l]);
if(newval == (*--tmp.end()).first ) continue;
else tmp.emplace_back(pair<int, int>(newval, id));
}
midA = tmp, tmp.clear();
tmp.emplace_back(pair<int, int>(b[l], l));
for(auto [val, id] : midB)
{
int newval = gcd(val, b[l]);
if(newval == (*--tmp.end()).first ) continue;
else tmp.emplace_back(pair<int, int>(newval, id));
}
midB = tmp, tmp.clear();
}
printf("%d %lld\n", ans, sum);
}
int main()
{
lg[0] = -1;
for(int i = 1; i <= 200000; ++i) lg[i] = lg[i >> 1] + 1;
int T = read();
while(T--) solve();
return 0;
}
E1. Subtangle Game (Easy Version)
设 表示第 步选择 时是否有必胜策略。
转移时,枚举 。
若存在 为真,则 为假。
若不存在,且 (第 步可以选择 ),则 为真。
发现可以二维前缀和优化, 过。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
int read()
{
int x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 305;
int l, n, m;
int a[N];
int b[N][N];
int dp[N][N][N];
void solve()
{
l = read(), n = read(), m = read();
for(int i = 1; i <= l; ++i) a[i] = read();
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
b[i][j] = read();
for(int i = 0; i <= l + 1; ++i)
for(int j = 0; j <= n + 1; ++j)
for(int k = 0; k <= m + 1; ++k)
dp[i][j][k] = 0;
for(int i = l; i >= 1; --i)
{
for(int j = n; j >= 1; --j)
for(int k = m; k >= 1; --k)
{
if(b[j][k] == a[i])
{
if(!dp[i + 1][j + 1][k + 1]) dp[i][j][k] = 1;
}
dp[i][j][k] = dp[i][j][k] + dp[i][j + 1][k] + dp[i][j][k + 1] - dp[i][j + 1][k + 1];
}
}
if(dp[1][1][1]) printf("T\n");
else printf("N\n");
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
E2. Subtangle Game (Hard Version)
考虑 的 做完前缀和后的新定义:第 步选择子矩阵 中的数,是否有必胜策略。
发现:在存在可以选出前 步时,若 为真, 也一定为真。
只需要对奇数和偶数分别考虑即可,设 表示偶数步选择子矩阵 中的数,最小的必胜步数(即最小的 ), 同理。
记 表示值 在 序列中第一次出现的位置。
转移时,若 ,且 ,则 。
意思是,第 步选了 ,且另一个人不存在 在不超过 步选矩阵 中的数 的情况下的必胜策略,那么这一步是必胜的,最小步数是 。
一点思考:同一个值在 序列中会出现多次,也会出现在奇数位和偶数位,为什么只考虑第一次出现?
在 矩阵中,同样的值,选择更靠近右下角的位置一定更优,因此对于 序列中的数,两个人是抢着选 ,因此只考虑每种值第一次出现的位置。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
int read()
{
int x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int inf = 0x3f3f3f3f;
const int N = 1505;
int l, n, m;
int a[N], b[N][N], last[N * N];
int dp0[N][N], dp1[N][N];
void solve()
{
l = read(), n = read(), m = read();
for(int i = 1; i <= l; ++i)
{
a[i] = read();
if(!last[a[i]]) last[a[i]] = i;
}
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
b[i][j] = read();
for(int i = 0; i <= n + 1; ++i)
for(int j = 0; j <= m + 1; ++j)
dp0[i][j] = dp1[i][j] = inf;
for(int i = n; i >= 1; --i)
for(int j = m; j >= 1; --j)
{
dp0[i][j] = min(dp0[i + 1][j], dp0[i][j + 1]);
dp1[i][j] = min(dp1[i + 1][j], dp1[i][j + 1]);
if(!last[b[i][j]]) continue;
if(last[b[i][j]] % 2 == 0 && dp1[i + 1][j + 1] > last[b[i][j]] + 1) dp0[i][j] = min(dp0[i][j], last[b[i][j]]);
if(last[b[i][j]] % 2 == 1 && dp0[i + 1][j + 1] > last[b[i][j]] + 1) dp1[i][j] = min(dp1[i][j], last[b[i][j]]);
}
if(dp1[1][1] == 1) printf("T\n");
else printf("N\n");
for(int i = 1; i <= l; ++i) last[a[i]] = 0;
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】