【Codeforces Round 918 (Div. 4) 】摸鱼记录

文章详细解析了CodeforcesRound918比赛中的四道问题,涉及字符串分割、区间和判断、树状数组应用以及图的最短路径问题,展示了使用动态规划、前缀和等技术求解的具体过程。
摘要由CSDN通过智能技术生成

Codeforces Round 918 (Div. 4) : link

Problem A

Problem B

Problem C

判断总和是否为平方数。

Problem D: link

定义一个语言只由a,b,c,d,e构成。其中a,e为Vowel,其余为consonants。给定字符串s,判断s是否由CVCVC形式的字符组成,还需对字符串分割。如ba属于CV形式,bab属于CVC形式。
思路: 我们可以定义vis数组,初始化为-1,vis[i] 不为-1时,表示字符i位置之前是可分割的,其值vis[i] 为转移过来的上一个位置。记录上一个路径就可以从结果反推出原来的分割情况。
code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const int mod = 998244353;

bool isVowel(char s){
    if(s == 'a' || s == 'e')
        return true;
    return false;
}

bool isConsonant(char s){
    if(s == 'a' || s == 'e')
        return false;
    return true;
}

void solve() {
    int n;
    string s;
    cin >> n >> s;
    vector<int> vis(n + 1, -1);
    vis[0] = 0;
    for(int i = 0; i < n; i++){
        char a, b, c;
        a = s[i];
        if(i + 1 < n){
            b = s[i + 1];
            if(isConsonant(a) && isVowel(b) && vis[i] != -1){
                vis[i + 2] = i;
            }
        }
        if(i + 2 < n){
            b = s[i + 1];
            c = s[i + 2];
            if(isConsonant(a) && isVowel(b) && isConsonant(c) && vis[i] != -1){
                vis[i + 3] = i;
            }
        }
    }   
    // for(int i = 0; i <= n; i++){
    //     cout << vis[i] << (i == n ? '\n' : ' ');
    // }
    set<int> path;
    int cur = n;
    while(cur){
        path.insert(cur);
        cur = vis[cur];
    }
    for(int i = 0; i < n; i++){
        if(i != 0 && path.find(i) != path.end()){
            cout << ".";
        }
        cout << s[i];
    }
    cout << "\n";
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

Problem E:link

对于长度为n的序列a.判断是否存在这样的一个区间[L,R],使得奇数位置的和等于偶数位置的和。(n<=2e5)
思路: 二分并不能满足题意,区间长度与是否满足奇偶数位置和无关。考虑前缀和,奇数位置做加法,偶数位置做减法。若能找到相同前缀和,则说明存在这样的一个区间。
code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const int mod = 998244353;

void solve() {
    int n;
    cin >> n;
    vector<ll> a(n + 1);
    vector<ll> pre(n + 1);
    for(int i = 1; i <= n; i++){
        cin >> a[i];
    }
    multiset<ll> s;
    for(int i = 0; i <= n; i++){
        pre[i] = (i == 0 ? 0 : pre[i - 1] + a[i] * (i % 2 == 1 ? 1 : -1));
        s.insert(pre[i]);
    }

    for(int i = 0; i <= n; i++){
        s.erase(s.find(pre[i]));
        if(s.find(pre[i]) != s.end()){
            cout << "YES\n";
            return ;
        }
    }
    cout << "NO\n";
    return ;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

Problem F:link

n个人,第i个人从 a i a_i ai 走到 b i b_i bi,题目保证 a i < b i , 且 a i , b i   d i s t i n c t a_i < b_i, 且a_i, b_i \: distinct ai<bi,ai,bidistinct。每个人的行进速度一致,走到 b i b_i bi后会停下。而当两个人走到同一个位置时会greeting一次。求greeting的总数。(n <= 2e5, a,b < 1e9)
思路:考虑如何达成greeting,两个人 i , j i, j i,j。当 a i < a j 且 b i > b j a_i < a_j 且 b_i > b_j ai<ajbi>bj 时, j会在 b j b_j bj停下,i后边会经过一次 b j b_j bj,达成一次greeting。注意greeting是相互的,两个人只计算一次即可。
非常朴素的想法是,先对a进行排序,这样后续的人都是在 a i a_i ai之后的位置出发的。那么对于第i个人来说,要寻找后续 b j < b i b_j < b_i bj<bi 的人的个数。这里的计数可以通过树状数组来计算,先将所有的b都计入,轮到 i i i的时候删掉 b i b_i bi的影响,再查询 [ a i , b i ] [a_i, b_i] [ai,bi]的区间和。这样就满足了上述的两个条件。当然, a i , b i a_i, b_i ai,bi的取值为1e9,做之前需要先离散化。
注意的是,树状数组不要在函数内部初始化,不然每个testcase都开辟一次空间很费时间,会直接TLE。
code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const int mod = 998244353;

int  find(int x, vector<int> &val){
    return lower_bound(val.begin(), val.end(), x) - val.begin() + 1;
}

struct Fenwick{
    const int n;
    vector<ll> a;
    Fenwick(int n): n(n + 10), a(n + 10, 0) {}
    void add(int x, ll v){
        for(int i = x; i <= n; i += i & -i){
            a[i] += v;
        }
    }

    ll sum(int x){
        ll res = 0;
        while (x)
        {
            res += a[x];
            x -= x & -x;
        }   
        return res;
    }

    ll rangeSum(int l, int r){
        return sum(r) - sum(l - 1);
    }

    void clear(){
        for(int i = 0; i < n; i++){
            a[i] = 0;
        }
    }
};
Fenwick BIT = Fenwick(maxn << 2);

void solve() {
    int n;
    cin >> n;
    vector<pair<int,int>> p(n);
    vector<int> val;
    for(int i = 0; i < n; i++){
        cin >> p[i].first >> p[i].second;
        val.push_back(p[i].first), val.push_back(p[i].second);
    }
    sort(val.begin(), val.end());
    val.erase(unique(val.begin(), val.end()), val.end());
    
    for(int i = 0; i < n; i++){
        p[i].first = find(p[i].first, val);
        p[i].second = find(p[i].second, val);
    }
    sort(p.begin(), p.end(), [&](const pair<int,int> &a, const pair<int,int> &b){
        if(a.first == b.first)
            return a.second < b.second;
        return a.first < b.first;
    });
    BIT.clear();
    for(int i = 0; i < n; i++){
        BIT.add(p[i].second, 1);
    }
    ll ans = 0;
    for(int i = 0; i < n; i++){
        BIT.add(p[i].second, -1);
        ll val = BIT.rangeSum(p[i].first, p[i].second);
        ans += val;
    }
    cout << ans << '\n';
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

Problem G:link

无向图有n个顶点,m条边,边权为w。Slavic从顶点1出发走到顶点n,在每个顶点可以获得一个自行车bike = s i s_i si,Slavic可以选择已获得的bike中的一个 s k s_k sk,从 i 到 j i到j ij的代价为 w i , j ∗ s k w_{i,j} * s_k wi,jsk。求走到顶点n的最小代价。(n <1e3, m<1e3)
思路:以第一个test case为例。S = {5,2,1,3,3}
test case
最优的结果为19,路线为1->2->3->2->4->5. 可以看到为了省力,在到达2后又去3得到了s=1,这之后都使用s=1的bike即最小代价。
我们可以定义下状态 dp[u][curs],表示走到顶点u,且当前持有最小的bike为curs时的代价。这个状态通过一条边w,转移到dp[v][new_s]。
d p [ v ] [ n e w s ] = d p [ u ] [ c u r s ] + w [ u ] [ v ] ∗ c u r s dp[v][new_s] = dp[u][curs] + w[u][v] * curs dp[v][news]=dp[u][curs]+w[u][v]curs

BFS更新dp状态即可,初始状态为dp[1][s[1]]。注意题目有重边。

code:

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 2e3 + 10;
const int mod = 998244353;

void solve() {
    int n, m;
    cin >> n >> m;
    // 有重边
    vector<pair<ll,ll>> vec[maxn];
    vector<vector<ll>> graph = vector<vector<ll>>(n + 1, vector<ll>(n + 1, 1e16));
    vector<ll> s(n + 1);
    for(int i = 0, u, v, w; i < m; i++){
        cin >> u >> v >> w;
        graph[u][v] = min(graph[u][v], 1ll * w);
        graph[v][u] = graph[u][v];
    }
    for(int i = 1; i <= n; i++){
        cin >> s[i];
    }

    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++){
            if(graph[i][j] != 1e16){
                vec[i].push_back({j, graph[i][j]});
                vec[j].push_back({i, graph[i][j]});
            }
        }
    }
    pair<ll,ll> st = make_pair(1ll, s[1]);
    queue< pair<ll,ll> > q;
    q.push(st);
    vector<vector<ll>> dp(n + 1, vector<ll>(1e3 + 10, 1e12));
    dp[1][s[1]] = 0;
    while(q.size()){
        auto p = q.front();
        q.pop();
        if(p.first == n){
            continue;
        }
        ll u = p.first, curs = p.second;
        for(auto U : vec[u]){
            auto v = U.first, w = U.second;
            ll temps = min(curs, s[v]);
            if(1ll * dp[u][curs] + 1ll * graph[u][v] * curs <  dp[v][temps]){
                dp[v][temps] = dp[u][curs] +  graph[u][v] * curs;
                q.push( {v, temps} );
            }
        }
    }
    ll ans = *min_element(dp[n].begin(), dp[n].end());
    // for(int i = 1; i <= n; i++){
    //     for(int j = 1; j <= 5; j++){
    //         cout << "i: " << i << " j: " << j << " dp[i][j]: " << dp[i][j] << '\n'; 
    //     }
    // }
    cout << ans << '\n';
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}
  • 27
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值