Educational Codeforces Round 168 (Rated for Div. 2)
A. Strong Password
给定一个字符串,若 ,则 的代价为2,否则为1。
向这个字符串中插入一个字符,使得代价最大。
在相邻相同的两个字符中间插入即可,没有相邻相同就在末尾插入,插入的字符与前后字符不同。
点击查看代码
#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 = 15;
int n;
char s[N];
void solve()
{
scanf("%s", s + 1);
n = strlen(s + 1);
int flag = 0;
for(int i = 1; i <= n; ++i)
{
if(!flag && s[i] == s[i - 1])
{
printf("%c", (s[i] - 'a' + 1) % 26 + 'a');
flag = 1;
}
printf("%c", s[i]);
}
if(!flag) printf("%c", (s[n] - 'a' + 1) % 26 + 'a');
printf("\n");
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
B. Make Three Regions
有一个 的房间,相邻房间可以相互到达,能够相互到达的房间构成一个区域,一些房间不能进入(经过)。保证读入的房间至多有一个区域。
问有多少个可进入的房间满足,删去这个房间后剩余房间构成三个区域?
只有当房间这样分布时,才满足条件。(或倒过来)
0 | 0 | 0 |
---|---|---|
1 | 0 | 1 |
点击查看代码
#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;
char s[2][N];
void solve()
{
n = read();
scanf("%s %s", s[0] + 1, s[1] + 1);
int cnt = 0;
for(int i = 2; i < n; ++i)
{
if(s[0][i] == '.' && s[1][i] == '.' && s[1][i - 1] == 'x' && s[1][i + 1] == 'x' && s[0][i - 1] == '.' && s[0][i + 1] == '.') ++cnt;
if(s[1][i] == '.' && s[0][i] == '.' && s[0][i - 1] == 'x' && s[0][i + 1] == 'x' && s[1][i - 1] == '.' && s[1][i + 1] == '.') ++cnt;
}
printf("%d\n", cnt);
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
C. Even Positions
给定一个残缺的长度为 的括号序列。( 为偶数)
所有奇数位残缺,填上残缺位使括号序列合法,且代价最小。
括号序列的代价定义为所有括号对的代价,一对括号对的代价定义为左右括号的距离。
对于每一个奇数位,贪心的填括号,如果前面有左括号,那么填右括号和它匹配。否则填右括号。
由于是奇数位,所以前面一定有偶数个左括号,若为0则填右括号。若不为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;
char s[N];
int n, l, r, q[N];
void solve()
{
n = read();
scanf("%s", s + 1);
l = 1, r = 0;
ll ans = 0;
for(int i = 1; i <= n; ++i)
{
if(s[i] == '_')
{
if(l > r) q[++r] = i;
else ans += i - q[l], ++l;
}else
{
if(s[i] == ')') ans += i - q[l], ++l;
else q[++r] = i;
}
}
printf("%lld\n", ans);
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
D. Maximize the Root
给定一颗以1为根的有根树,每个节点有一个初始权值 。
可以执行任意次如下操作:
选取一个节点 ,使 ,并使它的子树内所有其他节点 ,。
求 的最大值。
设 表示以 为根的子树中,最少的数为 。(也就是能被 操作 次)
转移时记 。
若 ,则 。
若 ,则 。
点击查看代码
#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];
vector<int> V[N];
int dp[N];
void dfs(int k, int fa)
{
dp[k] = a[k];
if(k == 1) dp[k] = 0x7fffffff;
int mn = 0x7fffffff;
for(auto v : V[k])
{
dfs(v, k);
mn = min(mn, dp[v]);
}
if(mn == 0x7fffffff) return ;
if(mn < dp[k]) dp[k] = mn;
else dp[k] = (dp[k] + mn) / 2;
}
void solve()
{
n = read();
for(int i = 1; i <= n; ++i) a[i] = read(), V[i].clear();
for(int i = 2; i <= n; ++i)
{
int fa = read();
V[fa].emplace_back(i);
}
dfs(1, 0);
printf("%d\n", dp[1] + a[1]);
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
E. Level Up
Monocarp在玩游戏,最初战斗力为1,他会依次挑战 只怪物。
若Monocarp的战力严格高于当前怪物,当前怪物会直接逃跑,否则会和Monocarp对决。
Monocarp每对决 次,战斗力会加1。
次询问,每次给定 和 ,问当升级间隔为 时,第 只怪物是否会和Monocarp对决?
容易发现,若第 只怪物能在升级间隔为 时和Monocarp对决,那么当升级间隔为 时也一定会和Monocarp对决,具有单调性。
注意到当升级间隔为 时,最多升级 ,总共会升级
次。
设 表示当升级间隔为 时,战斗力为 的最后一场对决的对手是第 只怪物。
转移时,需要找到区间 中战斗力大于 的第 只怪物。若没有则 。
可以权值线段树上二分实现 转移。
具体的,对于战斗力大于 的限制,可以通过改变枚举顺序,先枚举 ,并将小于等于 的数在权值线段树上删去。
对于区间左端点的限制,可以用前缀和做差,即先查出区间 中战斗力大于 的怪物数 ,并计算区间 上战斗力大于 的第 只怪物。
复杂度
我有一个复杂度为 整体二分的方法。
没有发现调和级数的性质,只能用单调性解决。
考虑如何求某个位置 能对决的最小的升级间隔 。
二分答案,每次 判断即可。
考虑整体二分,当二分答案为 时,有三种位置:
1.答案小于 。
2.答案在 中。
3.答案大于 。
如果每次处理复杂度是和第二种位置的数量相关,那么复杂度是可以接受的。
发现第一种一定会对决,第三种一定不会对决,考虑每个数记一个 ,表示从前一个第二种位置开始,到当前位置,一共会对决多少次。即两个2位置中间的所有1位置数 + 1(自己)。
发现每次处理只需要 遍历就好了,共 层,每层一共遍历 次,复杂度 。
代码
#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, q, a[N];
vector<int> pos[N];
vector<int> dp[N]; // 设dp[i][j]表示k=i时,第一次达到j时的位置
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
int sum[N << 2];
void update(int k, int l, int r, int pos, int val)
{
sum[k] += val;
if(l == r) return ;
int mid = (l + r) >> 1;
if(pos <= mid) update(ls(k), l, mid, pos, val);
else update(rs(k), mid + 1, r, pos, val);
}
int getpos(int k, int l, int r, int Size)
{
if(l == r) return l;
int mid = (l + r) >> 1;
if(sum[ls(k)] >= Size) return getpos(ls(k), l, mid, Size);
else return getpos(rs(k), mid + 1, r, Size - sum[ls(k)]);
}
int getsum(int k, int l, int r, int L, int R)
{
if(L <= l && r <= R) return sum[k];
int mid = (l + r) >> 1;
if(R <= mid) return getsum(ls(k), l, mid, L, R);
if(L > mid) return getsum(rs(k), mid + 1, r, L, R);
return getsum(ls(k), l, mid, L, R) + getsum(rs(k), mid + 1, r, L, R);
}
int main()
{
n = read(), q = read();
for(int i = 1; i <= n; ++i)
{
a[i] = read();
pos[a[i]].emplace_back(i);
dp[i].emplace_back(0), dp[i].emplace_back(i);
update(1, 1, n, i, 1);
}
for(int j = 1; j <= n; ++j)
{
for(auto x : pos[j]) update(1, 1, n, x, -1);
for(int i = 1; i * j <= n; ++i)
{
if(dp[i].size() <= j) continue;
if(dp[i][j] == n + 1) continue;
int sum1 = getsum(1, 1, n, 1, dp[i][j]);
int sum2 = getsum(1, 1, n, 1, n);
if(sum2 - sum1 < i){ dp[i].emplace_back(n + 1); continue; }
int pos = getpos(1, 1, n, sum1 + i);
dp[i].emplace_back(pos);
}
}
while(q--)
{
int i = read(), x = read();
if(dp[x].size() <= a[i]) printf("YES\n");
else if(i > dp[x][a[i]]) printf("NO\n");
else printf("YES\n");
}
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, Q;
int a[N], id[N], cnt[N], ans[N], vis[N], b[N];
void solve(int l, int r, int L, int R)
{
if(l > r) return ;
if(L == R){ for(int i = l; i <= r; ++i) ans[id[i]] = L; return ; }
int mid = (L + R) >> 1;
int tmp = 1, num = 0, last1 = 0, last2 = 0, pos1 = l - 1;
for(int i = l; i <= r; ++i)
{
num += cnt[id[i]] - 1;
tmp += num / mid, num %= mid;
if(a[id[i]] >= tmp) vis[id[i]] = 1, b[++pos1] = id[i],
last1 += cnt[id[i]], cnt[id[i]] += last2, last2 = 0,
++num, tmp += num / mid, num %= mid;
else vis[id[i]] = 0, last2 += cnt[id[i]] - 1,
cnt[id[i]] += last1, last1 = 0;
}
int pos2 = pos1;
for(int i = l; i <= r; ++i) if(!vis[id[i]]) b[++pos2] = id[i];
for(int i = l; i <= pos1; ++i) id[i] = b[i];
for(int i = pos1 + 1; i <= pos2; ++i) id[i] = b[i];
solve(l, pos1, L, mid), solve(pos1 + 1, pos2, mid + 1, R);
}
int main()
{
n = read(), Q = read();
for(int i = 1; i <= n; ++i) a[i] = read(), cnt[i] = 1, id[i] = i;
solve(1, n, 1, n);
for(int t = 1; t <= Q; ++t)
{
int y = read(), x = read();
if(ans[y] <= x) printf("YES\n");
else printf("NO\n");
}
return 0;
}
F. Chips on a Line
给定 ,在 上放 根薯条,使得它的最简形式是 根薯条。
有四种操作:
1.拿走 位置的一根薯条,并在 各放一根薯条。()
2.拿走 各一根薯条,并在 位置放一根薯条。()
3.拿走 位置的一根薯条,并在 位置放一根薯条。
4.拿走 位置的一根薯条,并在 位置放一根薯条。
最简形式指,通过任意次操作,使得薯条数最少。
发现可以转化为,从斐波那契数列的前 个数中选出可重复的 个数,使得它们的加和拆分成最少的斐波那契数的和,且最少个数为 个。
预处理 表示和为 最少需要多少个斐波那契数的和。
然后就是一个限制总个数的无限背包 。
设 表示只用前 个数,使用了 个,总和为 的方案数,将第一维省去。
很板,感觉不如 。
点击查看代码
#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 mod = 998244353;
const int M = 55005, N = 1005;
int n, m, x;
int num[M], fib[50];
void add(int &a, int b){ a = (a + b >= mod) ? (a + b - mod) : (a + b); }
int main()
{
n = read(), x = read(), m = read();
fib[1] = fib[2] = 1;
for(int i = 2; i <= 29; ++i) fib[i] = fib[i - 1] + fib[i - 2];
int max_size = fib[x] * n;
vector< vector<int> > dp(max_size + 1, vector<int>(n + 1));
for(int i = 1; i <= max_size; ++i)
for(int j = 29; j >= 2; --j)
if(i >= fib[j])
{
num[i] = num[i - fib[j]] + 1;
break;
}
dp[0][0] = 1;
for(int i = 1; i <= x; ++i)
for(int j = fib[i]; j <= max_size; ++j)
for(int k = 1; k <= n; ++k)
add(dp[j][k], dp[j - fib[i]][k - 1]);
int ans = 0;
for(int i = 1; i <= max_size; ++i)
if(num[i] == m) add(ans, dp[i][n]);
printf("%d\n", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】