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 许可协议。转载请注明出处!