SDUT 2022 Spring Individual Contest - K (个人赛补题)

G - Ganyu Segment Tree

题意:

有三种操作
每一个都有两种状态,可修改状态和不可修改状态
\(op1\),把\(a_p\)的状态修改为另一个状态
\(op2\)\([l,r]\)区间内的所有可修改数都\(×X\)
\(op3\) ,询问\([l,r]\)区间内的所有数能否整除\(X\),如果可以整除就把\([l,r]\)所有可修改都除以\(X\)

思路:

修改的数\(X\)很小,所以可以把\(X\)质因数分解,\([1,30]\)以内就\(10\)个质数,对于每个质数都开一个线段树,维护区间内当前质因子的个数,能否整除就看区间内当前质因子个数的最小值是否小于当前询问的\(X\)的质因子,只要区间最小值\(≥\)当前质因子个数,就再进行区间修改。
对于两种状态,就在线段树开两个值\(x,y\)f分别代表当前质因子区间个数的最小值,区间不可修改的数的质因子个数的最小值,对于第一个操作,就只需要把\(swap(x,y)\),每次查询的时候,就对区间的\((x,y)\)取最小值,每次修改操作不动区间内的\(y\)值,就是带质因数分解的区间修改区间查询的线段树。

View Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int inf = 1e18;
const int N = 1e5 + 10;
//  2  3   5  7  11 13 17 19 23 29
struct node {
    int l, r;
    int s1, s2;
    int add;
} tr[N * 4][12];
int n, m;
int a[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29};

void push_up(int u, int id) {
    tr[u][id].s1 = min(tr[u << 1][id].s1, tr[u << 1 | 1][id].s1);
    tr[u][id].s2 = min(tr[u << 1][id].s2, tr[u << 1 | 1][id].s2);
}

void push_down(int u, int id) {
    int add = tr[u][id].add;
    if (tr[u][id].add) {
        tr[u << 1][id].s1 += add, tr[u << 1 | 1][id].s1 += add;
        tr[u << 1][id].add += add, tr[u << 1 | 1][id].add += add;
        tr[u][id].add = 0;
    }
}
void build(int u, int l, int r, int id) {
    if (l == r) {
        tr[u][id] = {l, r, 0, inf, 0};
    } else {
        tr[u][id] = {l, r};
        int mid = l + r >> 1;
        build(u << 1, l, mid, id);
        build(u << 1 | 1, mid + 1, r, id);
        push_up(u, id);
    }
}

void modify(int u, int l, int r, int x, int id) {
    if (tr[u][id].l >= l && tr[u][id].r <= r) {
        tr[u][id].s1 += x;
        tr[u][id].add += x;
    } else {
        push_down(u, id);
        int mid = tr[u][id].l + tr[u][id].r >> 1;
        if (l <= mid) modify(u << 1, l, r, x, id);
        if (r > mid) modify(u << 1 | 1, l, r, x, id);
        push_up(u, id);
    }
}

int query(int u, int l, int r, int id) {
    if (tr[u][id].l >= l && tr[u][id].r <= r) {
        return min(tr[u][id].s1, tr[u][id].s2);
    } else {
        push_down(u, id);
        int mid = tr[u][id].l + tr[u][id].r >> 1;
        int res = inf;
        if (l <= mid) res = min(res, query(u << 1, l, r, id));
        if (r > mid) res = min(res, query(u << 1 | 1, l, r, id));
        return res;
    }
}

void change(int u, int pos, int id) {
    if (tr[u][id].l == tr[u][id].r && tr[u][id].l == pos) {
        swap(tr[u][id].s1, tr[u][id].s2);
    } else {
        push_down(u, id);
        int mid = tr[u][id].l + tr[u][id].r >> 1;
        if (pos <= mid)
            change(u << 1, pos, id);
        else
            change(u << 1 | 1, pos, id);
        push_up(u, id);
    }
}
signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    int l, r, x;
    for (int i = 1; i <= 10; i++) {
        build(1, 1, n, i);
    }
    while (m--) {
        string s;
        cin >> s;
        if (s == "mul") {
            cin >> l >> r >> x;
            for (int i = 1; i <= 10; i++) {
                int cnt = 0;
                if (x % a[i] == 0) {
                    while (x % a[i] == 0) {
                        cnt++;
                        x /= a[i];
                    }
                    modify(1, l, r, cnt, i);
                }
            }

        } else if (s == "flip") {
            int p;
            cin >> p;
            for (int i = 1; i <= 10; i++) {
                change(1, p, i);
            }
        } else {
            cin >> l >> r >> x;
            if (x == 1) {
                cout << "YES" << endl;
                continue;
            }
            bool flag = 1;
            int now=x;
            for (int i = 1; i <= 10; i++) {
                int cnt = 0;
                if (x % a[i] == 0) {
                    while (x % a[i] == 0) {
                        cnt++;
                        x /= a[i];
                    }
                    if (query(1, l, r, i) < cnt) {
                        flag = 0;
                    }
                }
            }
            if (flag == 0) {
                cout << "NO" << endl;
            } else {
                x=now;
                cout << "YES" << endl;
                for (int i = 1; i <= 10; i++) {
                    int cnt = 0;
                    if (x % a[i] == 0) {
                        while (x % a[i] == 0) {
                            cnt++;
                            x /= a[i];
                        }
                        modify(1, l, r, -cnt, i);
                    }
                }
            }
        }
    }
}

E - Easy Problem

题意:

给一张\(N*M\)的地图,有障碍物,现在给定两个机器人的坐标位置,只有上下左右的指令,这个指令只针对两个机器人同时进行的(同上下左右),问两个机器人相遇的最少操作数

思路:

由于地图范围很小,所以直接开四维,直接把两个点的坐标都存进去,\(dis(x1,y1,x2,y2)\)代表\(a\)\((x1,y1)\),\(b\)\((x2,y2)\)的这种状态的最小操作数,所以最终对\(dis(x,y,x,y)\)取最小值就行

View Code
#include <bits/stdc++.h>
using namespace std;
const int N = 60;
const int inf = 0x3f3f3f3f;
typedef pair<int, int> PII;
#define x first
#define y second
char s[N][N];
int n;
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
PII st, ed;
int dis[N][N][N][N];
int vis[N][N][N][N];
struct node {
    int a, b, c, d;
};
void bfs(PII st, PII ed) {
    memset(dis, 0x3f, sizeof dis);
    dis[st.x][st.y][ed.x][ed.y] = 0;
    queue<node> q;
    q.push({st.x, st.y, ed.x, ed.y});

    while (q.size()) {
        auto t = q.front();
        q.pop();
        int a = t.a, b = t.b, c = t.c, d = t.d;
        if (vis[a][b][c][d]) continue;
        vis[a][b][c][d] = 1;

        for (int i = 0; i < 4; i++) {
            int na = a + dx[i];
            int nb = b + dy[i];
            int nc = c + dx[i];
            int nd = d + dy[i];
            if (na < 1) na = 1;
            if (nb < 1) nb = 1;
            if (nc < 1) nc = 1;
            if (nd < 1) nd = 1;
            if (na > n) na = n;
            if (nb > n) nb = n;
            if (nc > n) nc = n;
            if (nd > n) nd = n;
            if (s[na][nb] == '*') na = a, nb = b;
            if (s[nc][nd] == '*') nc = c, nd = d;
            if (dis[na][nb][nc][nd] > dis[a][b][c][d] + 1) {
                q.push({na, nb, nc, nd});
                dis[na][nb][nc][nd] = dis[a][b][c][d] + 1;
            }
        }
    }
}
signed main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        scanf("%s", s[i] + 1);
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (s[i][j] == 'a') {
                st = {i, j};
            }
            if (s[i][j] == 'b') {
                ed = {i, j};
            }
        }
    }
    bfs(st, ed);
    int minv = inf;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            minv = min(minv, dis[i][j][i][j]);
        }
    }
    if (minv == inf)
        cout << "no solution" << endl;
    else
        cout << minv << endl;
}

J - IHI's Magic String

题意:

有三种操作
一,在一个字符串的末尾添加一个字符
二,删除末尾的一个字符(没字符就不操作)
三,使得所有的\(a\)字符都变成\(b\)字符
问操作完后,最终形成的字符串是是什么

思路:

先把所有操作都存在来,然后从后往前遍历,开个\(del\),表示需要删除多少个字符,每次遇见操作二\(+1\),每次遇到操作一,若没有\(del\)则添加字符,否则就\(del-1\)。开个\(fa(i)\)数组,每个颜色指向自己,每次遇见操作三,就使得颜色\(a\)需要指向颜色\(b\)所指向的。

View Code
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;
struct node {
    int op;
    char x, y;
} a[N];
int n;
int fa[N];
signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    string s;
    cin >> n;
    for (int i = 0; i < 26; i++) {
        fa[i] = i;
    }
    for (int i = 1; i <= n; i++) {
        cin >> a[i].op;
        if (a[i].op == 1) {
            cin >> a[i].x;
        } else if (a[i].op == 3) {
            cin >> a[i].x >> a[i].y;
        }
    }
    int bks = 0;
    for (int i = n; i >= 1; i--) {
        if (a[i].op == 1) {
            if (bks > 0) {
                bks--;
            } else {
                char x = fa[a[i].x - 'a'] + 'a';
                s.push_back(x);
            }
        } else if (a[i].op == 2) {
            bks++;
        } else {
            fa[a[i].x - 'a'] = fa[a[i].y - 'a'];
        }
    }
    if (s.empty()) {
        cout << "The final string is empty" << endl;
    } else {
        reverse(s.begin(), s.end());
        cout << s << endl;
    }
}

H - Optimal Biking Strategy

题意:

\(N\)个自行车站,每次骑车和放车都必须在自行车站进行(很重要),现在给你\(N\)个自行车站的位置,起点在\(0\),终点在\(p\),每耗\(1\)元钱,就可以骑车\(s\)公里,不满一公里,也消耗\(1\)元钱,现在给定\(s\)元,问你最少步行多少公里。

思路:

\(dp(i,j)\)代表考虑前\(i\)个车站,使用了\(j\)元,最多骑行多少公里。
现在用了\(j-k\)元,所以二分找到大于等于当前点往前\((j-k)*s\)的车站位置,为\(now\)
\(dp(i,j)=max(dp(i,j),dp(now,k)+a_i-a_{now})\),代表在\(now\)位置使用\(j-k\)元,骑到当前位置
\(dp(i,j)=max(dp(i-1,j))\),现在不用钱

View Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
int n, p, s, m;
int a[N];
int dp[N][6];
signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0) ;
    cout.tie(0) ;
    cin >> n >> p >> s;

    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    cin >> m;

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            for (int k = 0; k < j; k++) {
                int dis = s * (j - k);
                dis = a[i] - dis;
                int now = lower_bound(a+1, a + n + 1, dis) - a;
                dp[i][j] = max({dp[i][j], dp[now][k] + a[i] - a[now]});
            }
            
            dp[i][j] = max({dp[i][j], dp[i - 1][j]});
        }
    }

    int ans=0;
    for(int i=1;i<=n;i++) {
        ans=max(ans,dp[i][m]);
    }
    cout<<p-ans<<endl;
}
posted @ 2022-06-01 15:42  Wraith_G  阅读(89)  评论(0)    收藏  举报
// //