Contestant. Rank 2199. Rating +10.
A. Two Towers
题意
给定由两种不同颜色的元素叠成的塔,定义操作为将一个塔上的顶部元素移动到另一个塔,在若干次操作后,输出是否可以让两个塔的元素颜色相间。
思路
显然,这个问题可以抽象为:给定一个元素序列,找出一个点,将序列分成两半,使分割后的序列颜色相间。
那么,我们需要满足两个条件:
序列内没有连续
个及以上相同元素相邻; 序列内连续
个及以上相同元素相邻的组的个数最多只有 。
时间复杂度:
对应AC代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1010, inf = 0x3f3f3f3f;
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while(t --){
int a, b;
cin >> a >> b;
string x, y;
cin >> x >> y;
reverse(y.begin(), y.end());
x += y;
int n = a + b;
char pre = -1;
int cnt = 0;
bool ans = true, found = false;
for(int i=0;i<n;i++){
char cur = x[i];
if(cur == pre) {
cnt ++;
if(cnt == 2){
if(found){
ans = false;
break;
}
found = true;
}
if(cnt >= 3){
ans = false;
break;
}
}else cnt = 1;
pre = cur;
}
cout << (ans ? "YES\n" : "NO\n");
}
return 0;
}
漏条件了,淦
B. Ideal Point
题意
给定
思路
显然,若不希望让其他节点成为被最多数量的区间包含的点,我们就希望两个区间的交集为一个点,也就是说,我们只需删到最后只剩下
换句话说,我们只需找出这样的区间,满足左边界为
时间复杂度:
对应AC代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1010, inf = 0x3f3f3f3f;
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while(t --){
int a, b;
cin >> a >> b;
string x, y;
cin >> x >> y;
reverse(y.begin(), y.end());
x += y;
int n = a + b;
char pre = -1;
int cnt = 0;
bool ans = true, found = false;
for(int i=0;i<n;i++){
char cur = x[i];
if(cur == pre) {
cnt ++;
if(cnt == 2){
if(found){
ans = false;
break;
}
found = true;
}
if(cnt >= 3){
ans = false;
break;
}
}else cnt = 1;
pre = cur;
}
cout << (ans ? "YES\n" : "NO\n");
}
return 0;
}
nnd,一个水题卡半天
C. Tea Tasting
题意
给定
规定有
输出每位品茶师最后喝了多少。
思路
首先,我们可以发现,对于第
考虑到只有最后一个品茶师有可能喝到多余的茶,其余的品茶师喝的量都是次数乘上
当然,上述求和找下标的操作可以采用 前缀和+二分。
时间复杂度:
对应AC代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10, inf = 0x3f3f3f3f;
int a[N], b[N], sum[N], drunk[N], ans[N];
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while(t --){
int n;
cin >> n;
for(int i=1;i<=n;i++) cin >> a[i];
for(int i=1;i<=n;i++) {
cin >> b[i];
drunk[i] = 0;
ans[i] = 0;
}
sum[0] = drunk[0] = ans[0] = 0;
for(int i=1;i<=n;i++) sum[i] = sum[i - 1] + b[i];
for(int i=1;i<=n;i++) {
int x = lower_bound(sum + i, sum + n + 1, a[i] + sum[i - 1]) - sum - 1; //x为能吃的最后一个玩意儿
//if(x == 0) continue;
int left = a[i] + sum[i - 1] - sum[x];
if(x < n) ans[x + 1] += left;
drunk[i] ++;
drunk[x + 1] --;
}
for(int i=1;i<=n;i++){
drunk[i] += drunk[i - 1];
ans[i] += drunk[i] * b[i];
cout << ans[i] << ' ';
}
cout << '\n';
}
return 0;
}
过于模拟以至于思路很清晰也很单一
D. Triangle Coloring
题意
给定一个有
思路
显然,我们需要考虑一个连通块内权值重复的边,而我们关注的是最大值和次大值的情况:
全都相等,
种选择; 次大值和最小值相等,
种选择; 次大值和最大值相等,
种选择; 没有一个相等,
种选择。
照上述讨论,我们可以发现一个连通块的解等于最小值和多少条边是相等的。
而考虑到两种颜色的个数要相同,我们相当于在
时间复杂度:不会分析
对应AC代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e5 + 10, inf = 0x3f3f3f3f, mod = 998244353;
int a[N];
int exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1;
y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
int Inv(int a) {
int x, y;
exgcd(a, mod, x, y);
return (x % mod + mod) % mod;
}
int C(int m, int n) {
int a = 1, b = 1;
if (m < n) return 0;
while (n) {
a = (a * m) % mod;
b = (b * n) % mod;
m--;
n--;
}
return a * Inv(b) % mod;
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
int t = n / 3;
int ans = C(t, t / 2);
for (int i = 0; i<=n/3-1;i++) {
int minn = min(a[i * 3 + 3], min(a[i * 3 + 1], a[i * 3 + 2]));
int cnt = 0;
for (int j = 1; j <= 3; j++)
if (a[i * 3 + j] == minn) cnt++;
ans = (ans * cnt) % mod;
}
cout << ans << '\n';
return 0;
}
数组不要开小了!!
E. Explosions?
题意
给定一个数组
若怪物在遭到攻击后血量小于等于
定义”爆炸”为一个死亡的怪物将相邻的怪物的血量扣去
规定只能让”爆炸”发生一次或不发生,输出让所有怪物死亡的
思路
答案的推导
首先,我们暂定爆炸的那个点下标为
我们不妨用补集的思路,算出最后满足上述条件的数组
总和的计算
首先,不难发现对于
“左边界”
对于
显然,因为严格单调递增,那么 $hp > i
所以,对于左边界
当然,若没有
递推出总和
注意一下推导左边界的条件:$h{p - 1} > h p - 1$。这个条件有一个特点:我们目前所求得的”左边界”实际上是严格单调递增且相邻差值为
但是,不难发现
更具体地说,
有疑惑吗?请留意条件:$h{p - 1} \leq h p - 1
计算
综上,我们只需对每个”严格单调递增且相邻差值为
因而,
“左边界”的低复杂度求法
考虑到我们不可以在每次递推的时候都向前遍历满足条件的”左边界”
我们回归到条件
最后的答案
显然,上述操作是线性的,那么我们只需遍历所有的
当然,因为
时间复杂度:
对应AC代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
const int N = 3e5 + 10, inf = 0x3f3f3f3f, mod = 998244353;
int hl[N], hr[N], dp[2][N];
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while(t --){
int n;
cin >> n;
int sum = 0;
for(int i=0;i<n;i++) {
cin >> hl[i];
sum += hr[n - i - 1] = hl[i];
dp[0][i] = dp[1][i] = 0;
}
for(int tp=0;tp<2;tp++){
int *h = tp == 0 ? hl : hr;
stack<pii> s;
for(int i=0;i<n;i++){
while(!s.empty() && s.top().first > h[i] - i) s.pop();
int j = max(-1LL, i - h[i]);
if(!s.empty()) j = max(j, s.top().second);
int len = i - j;
dp[tp][i] = len * h[i] - len * (len - 1) / 2;
if(j >= 0 && len < h[i]) dp[tp][i] += dp[tp][j];
s.emplace(h[i] - i, i);
}
}
int ans = 0;
for(int i=0;i<n;i++){
ans = max(ans, dp[0][i] + dp[1][n - i - 1] - hl[i] * 2);
}
cout << sum - ans << '\n';
}
}
好复杂.jpg
- 本文链接 https://floating-ocean.github.io/blog_old/posts/3290967404/
- 版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!