Loading

AtCoder Beginner Contest 322




B - Prefix and Suffix

难度: ⭐

题目大意

给定两个字符串t和s, 如果t是s的前缀则输出1, 如果是后缀则输出2, 如果都是则输出0, 都不是则输出3;

解题思路

暴力即可;

神秘代码

#include<bits/stdc++.h>
#define int l1ng l1ng
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353;
typedef pair<int, int> PII;
int n, m, idx;
signed main() {
    string s,t;
    cin>>n>>m>>s>>t;
    string pr=t.substr(0,n);
    string su=t.substr(m-n,n);
    if(pr==s&&su==s) cout<<0;
    else if(pr==s) cout<<1;
    else if(su==s) cout<<2;
    else cout<<3;
    return 0;
}




C - Festival

难度: ⭐

题目大意

一个城市计划在n天中的m天放烟花, 并且给出这m天; 对于这n天中的每一天, 输出其距离未来最近的放烟花的一天还有几天;

解题思路

双指针暴力即可;

神秘代码

#include<bits/stdc++.h>
#define int l1ng l1ng
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353;
typedef pair<int, int> PII;
int n, m, idx;
int f[N];
signed main() {
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>f[i];
    idx=1;
    for(int i=1;i<=n;i++){
        while(i>f[idx]) idx++;
        cout<<f[idx]-i<<endl;
    }
    return 0;
}




D - Polyomino

难度: ⭐⭐⭐

题目大意

给定三个几何图形, 保证这三个几何图形的大小不会超出一个4*4的方格, 请问能否用这三个图形拼出一个4 * 4的正方形;

解题思路

一道很复杂的暴力模拟, 这天实在是没心思看了, 等以后再补吧...未来可期题解

神秘代码





E - Pr1duct Devel1pment

难度: ⭐⭐⭐

题目大意

一个项目需要k个物品都至少到达p个数量; 现在有n个套餐, 每个套餐都可以给这k个物品加若干个数量, 每个套餐都要各自的价格; 请问在满足项目要求的情况下需要的最小花费是多少;

解题思路

一个很明显的dp, 并且我们发现k和p的数据范围最大是5, 那我们可以直接开一个6维数组, 并且用前i个套餐满足各个属性达到各个值所需要的最小花费; 一开始我还在想因为不确定k所以不知道数组开几维, 后来一想不用考虑这个, 对于多出来的维度我们把他们的要求设为0就可以了, 并且用滚动数组可以把第1维也省掉, 所以开5维数组就够了; 因为用滚动数组, 所以我们遍历状态时要倒序遍历, 并且我们要求是大于等于p, 所以状态转移时小于0的状态也是可以的, 我们可以把这些都归到0的状态上即可;

神秘代码

#include<bits/stdc++.h>
#define int l1ng l1ng
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353;
typedef pair<int, int> PII;
int n, m, idx;
int f[N], p[110][10];
int dp[6][6][6][6][6];
signed main() {
    int k;
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i++) {
        cin >> f[i];
        for (int j = 1; j <= m; j++) {
            cin >> p[i][j];
        }
    }
    for (int h1 = 0; h1 <= k; h1++) {
        for (int h2 = 0; h2 <= k; h2++) {
            for (int h3 = 0; h3 <= k; h3++) {
                for (int h4 = 0; h4 <= k; h4++) {
                    for (int h5 = 0; h5 <= k; h5++) {
                        dp[h1][h2][h3][h4][h5] = 1e12 + 10;
                    }
                }
            }
        }
    }
    dp[0][0][0][0][0] = 0;
    for (int i = 1; i <= n; i++) {
        for (int h1 = k; h1 >= 0; h1--) {
            for (int h2 = k; h2 >= 0; h2--) {
                for (int h3 = k; h3 >= 0; h3--) {
                    for (int h4 = k; h4 >= 0; h4--) {
                        for (int h5 = k; h5 >= 0; h5--) {
                            int x1 = max(h1 - p[i][1], 0ll);
                            int x2 = max(h2 - p[i][2], 0ll);
                            int x3 = max(h3 - p[i][3], 0ll);
                            int x4 = max(h4 - p[i][4], 0ll);
                            int x5 = max(h5 - p[i][5], 0ll);
                            dp[h1][h2][h3][h4][h5] = min(dp[h1][h2][h3][h4][h5], dp[x1][x2][x3][x4][x5] + f[i]);
                        }
                    }
                }
            }
        }
    }
    int d[6];
    for (int i = 1; i <= 5; i++) {
        if (i <= m) d[i] = k;
        else d[i] = 0;
    }
    int res = dp[d[1]][d[2]][d[3]][d[4]][d[5]];
    if (res == 1e12 + 10) res = -1;
    cout << res;
    return 0;
}




F - Vacation Query

难度: ⭐⭐⭐⭐

题目大意

给定一个由0/1组成的数列, 我们可以对其进行n次操作, 操作分为两种, 每种都给出一个区间, 一是输出这个区间中连续的1的最长长度; 二是将这个区间里的1变成0, 0变成1;

解题思路

很明显的一个线段树, 我们需要维护8个值: 区间边界: l, r; 翻转标记: t, 区间长度: k; 区间中最长连续1/0的长度: m1, m0; 区间前缀/后缀连续1的长度: l1, r1; 区间前缀/后缀连续0的长度: l0, r0; 需要维护前缀和后缀是因为方便合并区间时计算最长的连续1的长度;
build函数就不多说了, 正常写就行;
pushup函数中考虑区间合并, m1, m0, l1, r1, l0, r0都可能改变, 所以需要一个一个更新; 对于前缀连续1的长度L1, 如果左区间的l1的长度等于区间长度(也就是整个区间都是1), 那么L1就可以更新为左区间的长度加上右区间的l1; 否则的话就直接把L1赋值为左区间的l1即可; 其他的前缀和后缀都同理; 对于M1来说, 他有三种可能, 一是左区间的m1, 二是右区间的m1, 三是左区间的后缀1和右区间的前缀1所组成的新的连续的1; 三者取最大值即可, m0同理;
pushdown函数主要作用是区间翻转, 如果根区间的t=1说明左右区间都需要翻转, 首先把左右区间各自的翻转标记k取反, 然后把m1和m0, l1和l0, r1和r0都交换即可;
modify函数的作用也是区间翻转, 不同于之前线段树区间求和的板子, 本题需要找到具体的区间才能对其进行修改, 所以此处的写法不同于模板;
query函数变化最大, 不同于模板, 这里如果函数的返回值只返回一个数无法去求区间的连续1的最长长度, 所以我们让query函数的返回值设为结构体类型; 和modify函数一样我们需要找到准确的区间, 所以当给定区间横跨左右区间时我们需要构建一个新的节点, 这个节点的区间范围和给定的范围一致, 节点各项值的设置和pushdown函数一致;最后输出这个节点的m1即可;

神秘代码

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 5e5 + 10, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int n, m, k;
string s;
int res = 0;
struct node{
    int l, r;
    int fir1, las1;
    int fir0, las0;
    int max1, max0;
    bool rev;
}tr[4 * N];
void Swap(int u){
    swap(tr[u].fir1, tr[u].fir0);
    swap(tr[u].las1, tr[u].las0);
    swap(tr[u].max1, tr[u].max0);
}
void pushup(int u){
    auto &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
    int x = max(left.max1, right.max1);
    root.max1 = max(x, left.las1 + right.fir1);
    if(left.max0) root.fir1 = left.fir1;
    else root.fir1 = left.fir1 + right.fir1;
    if(right.max0) root.las1 = right.las1;
    else root.las1 = left.las1 + right.las1;
    
    int y = max(left.max0, right.max0);
    root.max0 = max(y, left.las0 + right.fir0);
    if(left.max1) root.fir0 = left.fir0;
    else root.fir0 = left.fir0 + right.fir0;
    if(right.max1) root.las0 = right.las0;
    else root.las0 = left.las0 + right.las0;
}
void pushdown(int u){
    auto &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
    if(root.rev){
        left.rev = !left.rev;
        right.rev = !right.rev;
        Swap(u << 1);
        Swap(u << 1 | 1);
        root.rev = false;
    }
}
void build(int u, int l, int r){
    if(l == r){
        if(s[l] == '1') tr[u] = {l, r, 1, 1, 0, 0, 1, 0, false};
        else tr[u] = {l, r, 0, 0, 1, 1, 0, 1, false};
    }
    else{
        tr[u] = {l, r};
        tr[u].rev = false;
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}
void modify(int u, int l, int r){
    if(l <= tr[u].l && r >= tr[u].r){
        tr[u].rev = !tr[u].rev;
        Swap(u);
    }
    else{
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if(l <= mid) modify(u << 1, l, r);
        if(r > mid) modify(u << 1 | 1, l, r);
        pushup(u);
    }
}
node query(int u, int l, int r){
    if(l <= tr[u].l && r >= tr[u].r){
        return tr[u];
    }
    else{
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        bool f1 =false, f2 =false;
        node a, b;
        if(l <= mid) {
            f1 = true;
            a = query(u << 1, l, r);
        }
        if(r > mid) {
            f2 = true;
            b = query(u << 1 | 1, l, r);
        }
        if(f1 && f2){
            node c;
            c = {a.l, b.r};
            c.max1 = max(max(a.max1, b.max1), a.las1 + b.fir1);
            if(a.max0 == 0) c.fir1 = a.fir1 + b.fir1;
            else c.fir1 = a.fir1;
            if(b.max0 == 0) c.las1 = b.las1 + a.las1;
            else c.las1 = b.las1;
            return c;
        }
        else if(f1) return a;
        else if(f2) return b;
    }
}
signed main(){
    cin >> n >> m;
    cin >> s;
    s = ' ' + s;
    build(1, 1, n);
    while(m--){
        int a, b, c;
        cin >> a >> b >> c;
        if(a == 1){
            modify(1, b, c);
        }
        else {
            node res = query(1, b, c);
            cout << res.max1 << endl;
        }
    }
    return 0;
}
posted @ 2023-11-10 19:44  mostimali  阅读(38)  评论(0编辑  收藏  举报