【22暑期复建1】 Codeforces Round #791 (Div. 2)

【22暑期复建1】 Codeforces Round #791 (Div. 2)

传送门Codeforces Round #791 (Div. 2)

真的好久不写cf,一些疯狂踩坑

A - AvtoBus

prob.:

有很多车,有些车是4个轮子,有些是6个轮子,一共有n个轮子,问最少可能有几辆车,最多可能有几辆车?

idea:

分类讨论

注意判断无解的情况

code:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

signed main() {
//    cerr << (998244353998244352 / 2 - 3) / 2 << endl;
    ll _;
    cin >> _;
    while (_--) {
        ll n;
        cin >> n;
        if (n & 1 || n < 4) {
            cout << -1 << endl;
            continue;
        }
        if ((n / 2) & 1) {
            ll tmp = n / 6;
            if (tmp & 1) {
            } else {
                tmp--;
            }
            ll a = (n / 2 - tmp * 3) / 2;
            ll b = (n - a * 4) / 6;
            cout << a + b << " " << " " << (n / 2 - 3) / 2  + 1<< endl;
        } else {
            ll tmp = n / 6;
            if (!(tmp & 1)) {}
            else {
                tmp--;
            }
            ll a = (n / 2 - tmp * 3) / 2;
            ll b = (n - a * 4) / 6;
            cout << a + b << " " << (n / 4) << endl;
        }
    }

    return 0;
}

B - Stone Age Problem

prob.:

n个数,两种操作,1-把第i个数换成x,2-把所有数都换成x,问每次操作后n个数的和是多少

idea:

线段树肯定是能做的,但这才B题

存一个当前所有数的相同时的值pre,(即操作2的x值)

对于次操作用map存一下把当前位置的数变成了什么,计算和的时候map里面存的值相当于是在上次2操作的基础上改了的位置,且每个位置只存了最后一次改动的值

code:

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

typedef long long ll;

map<ll, ll> mp;


signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    ll n, q;
    cin >> n >> q;
    ll sum = 0;
    ll pre = -1;
    for(ll i = 1;i <= n; ++ i) {
        ll x;
        cin >> x;
        mp[i] = x;
        sum += x;
    }
    while(q--) {
        ll op ;
        cin >> op;
        if(op == 1) {
            ll pos, x;
            cin >> pos >> x;
            if(mp.find(pos) != mp.end()) {
                sum -= mp[pos];
            }
            else {
                sum-=pre;
            }
            sum += x;
            cout << sum << endl;
            mp[pos] = x;
        }
        else {
            ll x;
            cin >> x;
            pre = x;
            sum = x * n;
            mp.clear();
            cout << sum << endl;
        }
    }
    return 0;
}

C - Rooks Defenders

prob. :

有一个 n × n n \times n n×n的棋盘,有三种操作,1-在 ( x , y ) (x, y) (x,y)上放一个Rock,它会影响到这个与它x轴或y轴坐标的所有点(标记?),2-拿走 ( x , y ) (x, y) (x,y)上的一个Rock,保证不会有无效操作,3-问一个矩阵范围内的所有点是否都被标记

idea:

x轴和y轴分开考虑,每种全部标记的情况要不就是x轴完全标记要不就是y轴完全标记,可以自己画小样例理解一下

以x轴为例,要达成一段区间内的完整标记,考虑通过树状数组来完成,(树状数组区间和等于区间长度则为完整标记)

注意当某一个位置有多个值的时候,这个位置只考虑一次,所以加一个cnt数组辅助,当第一个加的时候和最后一个改的时候才对树状数组作更改

code:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const ll N = 2e5 + 10;

int cntX[N], cntY[N];
ll treeRow[N << 2], treeCol[N << 2];
ll n;

inline ll lowbit(ll x) {
    return x & (-x);
}

void updateRow(ll i, ll x) {
    while (i <= n) {
        treeRow[i] += x;
        i += lowbit(i);
    }
}

ll queryRow(ll i) {
    ll res = 0;
    while (i) {
        res += treeRow[i];
        i -= lowbit(i);
    }
    return res;
}

void updateCol(ll i, ll x) {
    while (i <= n) {
        treeCol[i] += x;
        i += lowbit(i);
    }
}

ll queryCol(ll i) {
    ll res = 0;
    while (i) {
        res += treeCol[i];
        i -= lowbit(i);
    }
    return res;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    ll q;
    cin >> n >> q;
    while (q--) {
        ll op;
        cin >> op;
        if (op == 1) {
            ll x, y;
            cin >> x >> y;
            cntX[x]++;
            cntY[y]++;
            if (cntX[x] == 1) updateRow(x, 1);
            if (cntY[y] == 1) updateCol(y, 1);
        } else if (op == 2) {
            ll x, y;
            cin >> x >> y;
            cntX[x]--;
            cntY[y]--;
            if (cntX[x] == 0) updateRow(x, -1);
            if (cntY[y] == 0) updateCol(y, -1);
        } else {
            ll x1, x2, y1, y2;
            cin >> x1 >> y1 >> x2 >> y2;
            bool flag = 0;
            if (queryRow(x2) - queryRow(x1 - 1) == x2 - x1 + 1) flag = 1;
            if (queryCol(y2) - queryCol(y1 - 1) == y2 - y1 + 1) flag = 1;
            if (flag) cout << "Yes\n";
            else cout << "No\n";
        }
    }
    return 0;
}

D - Toss a Coin to Your Graph…

prob.:

给一个有向图,每个点有一个点权,问长度超过k的路径中最大的点权最小值是多少

idea:

二分

二分最大点权值建图,然后对于每个图里面判有没有环,有环就可行,否则拓扑序找最长路径看是否大于k

code:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 2e5 + 10;

int a[N];
int n, m;
ll k;

struct edge {
    int from, to;
};

edge edges[N];

vector<int> g[N];
bool vis[N], instack[N];
int val[N], din[N];
bool haveLoop = false;

void dfs(ll x) {
    vis[x] = instack[x] = true;
    for (auto to : g[x]) {
        if (!vis[to]) dfs(to);
        else if (instack[to]) {
            haveLoop = true;
        }
    }
    instack[x] = false;
}

bool check(int mid) {
    for (int i = 0; i < N; ++i) {
        g[i].clear();
        vis[i] = false;
        din[i] = 0;
        val[i] = -1;
        instack[i] = 0;
    }
    haveLoop = false;


    for (int i = 1; i <= m; ++i) {
        int from = edges[i].from;
        int to = edges[i].to;
        if (a[from] <= mid && a[to] <= mid) {
            g[from].push_back(to);
            din[to]++;
        }
    }



    // find cir ,if have -> true


    for(int i = 1;i <= n; ++ i) {
        if(!vis[i]) dfs(i);
    }
//    dfs(0);
    if (haveLoop) return true;

    // the longest path >= k?

    for (int i = 1; i <= n; ++i) {
        if (din[i] == 0) {
            g[0].push_back(i);
            din[i]++;
        }
    }

    queue<int> que;
    que.push(0);
    val[0] = 0;

    int res = -1;
    while (!que.empty()) {
        int tmp = que.front();
        que.pop();
        for (auto to : g[tmp]) {
            din[to]--;
            val[to] = max(val[to], val[tmp] + 1);
            res = max(res, val[to]);
            if (din[to] == 0) que.push(to);
        }
    }

    return (res >= k);
}

signed main() {

//    cin >> n >> m >> k;
    scanf("%d%d%lld", &n, &m, &k);

    int mn = 0x3f3f3f3f;
    for (ll i = 1; i <= n; ++i) {
        cin >> a[i];
        mn = min(mn, a[i]);
    }

    if (k == 1ll) {
        printf("%d", mn);
//        cout << mn << endl;
        return 0;
    }

    for (int i = 1; i <= m; ++i) {
//        cin >> edges[i].from >> edges[i].to;
        scanf("%d%d", &edges[i].from, &edges[i].to);
    }

    int l = 0, r = 1e9 + 10, ans = -1;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid)) {
            ans = mid;
            r = mid - 1;
        } else {
            l = mid + 1;
        }
    }

    printf("%d", ans);
//    cout << ans ;
    return 0;
}

E - Typical Party in Dorm

prob.:

给一个由前17个小写字母组成的string s,同时含有一些"?“, 若干次询问,每次给出一个字母集合,可以由这些字母填充”?",问回文串的总个数

idea:

对于每个子串作为回文串时考虑有多少"?“是能随意填的,包括子串内部的对称的”?“和子串外部的所有”?"

对于每个子串考虑它至少需要的字母集合,即以当前子串为回文串时有些“?”是固定的,它们会用到一些字母

在至少的字母集合上考虑sosdp(结合数据范围可以想到)

刚开始想的是维护每个子集可以随意填的“?”的个数然后随着sosdp更新,这样的转移长度极端条件是500,必炸

对于每个子串于最小字母子集,直接将子串对于 包括当前最小字母子集 且长度为len的字母集合 的贡献算出来,统一转移,这样的转移长度是log的(即17)

第一次自己做出2400的题(*冲冲冲冲

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int mod = 998244353;

//map<pair<int, int>, ll> dp;
ll dp[(1 << 17) + 10][20];

ll qpow(ll x, ll n) {
    ll res = 1;
    while (n) {
        if (n & 1) res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

ll getCnt(ll x) {
    int res = 0;
    while (x) {
        if (x & 1)res++;
        x >>= 1;
    }
    return res;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    int n;
    cin >> n;
    string s;
    cin >> s;
    int tot = 0;
    for (auto ch : s) {
        if (ch == '?') tot++;
    }
    for (int left = 0; left < n; ++left) {
        int ll = left, rr = left;
        int tmpPos = 0;
        int cnt = 0;
        int num = 0;
        while (1) {
//            cerr <<"lll rr" <<ll << " " <<rr << endl;
            if (ll < 0 || rr >= n) break;
            if (s[ll] == '?' && s[rr] == '?') {
                cnt++;
                num += 2;
                if (ll == rr) {
                    num--;
                }
            } else if (s[ll] == '?' || s[rr] == '?') {
                char ch = s[ll] == '?' ? s[rr] : s[ll];
                tmpPos |= (1 << (ch - 'a'));
                num++;
            } else if (s[ll] != s[rr]) {
                break;
            } else {
//                continue;
            }
            int count = getCnt(tmpPos);
            for (int len = count; len <= 17; ++len) {
                dp[tmpPos][len] = (dp[tmpPos][len] + qpow(len, tot - num + cnt)) % mod;
//                dp[{tmpPos, len}] = (dp[{tmpPos, len}] + qpow(len, tot - num + cnt)) % mod;
            }

            ll--;
            rr++;
        }

        ll = left, rr = left + 1;
        tmpPos = 0;
        cnt = 0;
        num = 0;
        while (1) {
//            cerr <<"lll rr" <<ll << " " <<rr << endl;
            if (ll < 0 || rr >= n) break;
            if (s[ll] == '?' && s[rr] == '?') {
                cnt++;
                num += 2;
                if (ll == rr) num--;
            } else if (s[ll] == '?' || s[rr] == '?') {
                char ch = s[ll] == '?' ? s[rr] : s[ll];
                tmpPos |= 1 << (ch - 'a');
                num++;
            } else if (s[ll] != s[rr]) {
                break;
            } else {
//                continue;
            }
            int count = getCnt(tmpPos);
            for (int len = count; len <= 17; ++len) {
                dp[tmpPos][len] = (dp[tmpPos][len] + qpow(len, tot - num + cnt)) % mod;
//                dp[{tmpPos, len}] = (dp[{tmpPos, len}] + qpow(len, tot - num + cnt)) % mod;
            }

            ll--;
            rr++;
        }
    }

    int nnn = 17;
    for (int i = 0; i < nnn; ++i)
        for (int mask = 0; mask < (1 << nnn); ++mask) {
            if (mask & (1 << i)) {
                for (int len = 0; len <= nnn; ++len) {
                    dp[mask][len] = (dp[mask][len] + dp[mask ^ (1 << i)][len]) % mod;
//                    dp[{mask, len}] = (dp[{mask, len}] + dp[{mask ^ (1 << i), len}]) % mod;
                }
            }
        }

//    for(int mask = 0 ; mask < ( 1<< nnn); ++ mask) {
//        for(int len = 0; len <= nnn; ++ len) {
//            cout << dp[{mask, len}] << " ";
//        }
//        cout << endl;
//    }

    int q;
    cin >> q;
    while (q--) {
        string t;
        cin >> t;
        int tmpPos = 0;
        for (auto ch : t) {
            tmpPos |= (1 << (ch - 'a'));
        }
//        cout << dp[{tmpPos, t.size()}] << endl;
        cout << dp[tmpPos][t.size()] << endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值