Codeforces Round 918 (Div. 4)题解(A-G)

文章描述了几种C++函数,涉及字符串操作、条件判断、输入输出、字符处理、前缀和算法、离散化、树状数组以及Dijkstra最短路径算法的应用,展示了编程中常见问题的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A:

void solve()
{
    int a, b, c; cin>>a>>b>>c;
    if(a==b) cout<<c;
    else if(b==c) cout<<a;
    else cout<<b;
    cout<<'\n';
}

B:

string s[4];
map<char, int> mp;
void solve()
{
    mp.clear();
    for(int i=1; i<=3; ++i)
        cin>>s[i];
    for(int i=1; i<=3; ++i)
        for(int j=0; j<3; ++j)
            if(s[i][j]!='?') mp[s[i][j]]++;
    for(auto x:mp)
        if(x.second==2) {cout<<x.first<<'\n'; return ;}
}

C:

int n, x;
void solve()
{
    cin>>n;
    ll sum = 0;
    for(int i=1; i<=n; ++i) cin>>x, sum += x;
    if(sum/(ll)sqrt(sum)==sqrt(sum)) cout<<"YES"<<'\n';
    else cout<<"NO"<<'\n';
}

D:

关键在于判断字符串尾部的越界问题,可能会导致 ' . ' 的添加位置出现错误,也会影响最后对s输出长度。

思路:

判断' . '的位置从元音的出现位置i入手,如果s[i+2]是辅音,证明这是一个"辅元辅"的单词,否则是一个"辅元"的单词。

特判:

在字符串结尾添加一个辅音,如果现在s以"辅元辅辅"结尾,此时会在倒数第二个位置添加一个'.'如果以"辅元辅"结尾,s[i+2]为空,所以也会在倒数第二个位置添加一个'.'。

最后只需要输出s.size()-2的字符串就好了。

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(0); cin.tie(0), cout.tie(0)

int n;
string s;

void solve()
{
    cin>>n>>s;
    s += 'b';//为尾部插入一个辅音,保证无论以什么形式结尾,倒数第二个字符都将会是'.'
    for(int i=0; i<s.size(); ++i)//此处一定要用s.size()或s.length(),字符串的长度在随时更新
        if(s[i]=='a' or s[i]=='e') {
            if(s[i+2]=='b' or s[i+2]=='d' or s[i+2]=='c')
                s.insert(i+2, 1, '.');
            else//此处等效于s[i+2]为元音 or 为空(越界)
                s.insert(i+1, 1, '.');
            ++i;
        }
    cout<<s.substr(0, s.size()-2)<<'\n';
}

signed main(void)
{
    ios;
    int t=1; cin>>t;
    while(t--) solve();
    return 0;
}

E:

算法:前缀和

看数据范围可知,暴力O(n^2)会t,必须实现O(nlog(n))以内的查询方法,因为是连续区间问题,由此可想到前缀和,树状数组,线段树等。本题选择简单的前缀和。

思路:

先模拟题意,构造一个前缀和数组,奇 + 偶 - (反过来也可以)

如果此时a[i]==0,可知区间[1, i]是满足题意的,结束solve。

否则用map记录a[i]的值,当map[a[i]]出现第二次时,代表区间 [1, r1] 与区间 [1, r2] 的前缀和是相等的,因此区间 [r1, r2] 的值为两区间相减==0, 满足题意,结束solve()。

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(0); cin.tie(0), cout.tie(0)
typedef long long ll;
const int MAXN=200010;

ll n, a[MAXN];
map<ll, int> mp;

void solve()
{
    cin>>n;
    mp.clear();
    for(int i=1; i<=n; ++i) cin>>a[i];
    for(int i=1; i<=n; ++i) 
    {
        if(i%2==1) a[i] = a[i-1]+a[i];
        else a[i] = a[i-1]-a[i];
        if(a[i]==0) {cout<<"YES"<<endl; return ;}
        mp[a[i]]++;
        if(mp[a[i]]>=2) {cout<<"YES"<<endl; return ;}
    }
    cout<<"NO"<<endl;
}

signed main(void)
{
    ios;
    int t=1; cin>>t;
    while(t--) solve();
    return 0;
}

F:

算法:离散化,树状数组 

一维线段之间的相遇关系,对于线段(x1, x4), (x2, x3) 只有 x1<x2 并且 x4>x3 时才会相遇。

思路:

因为数据范围过大,因此首先考虑离散化,用一条线段树(bushi扫描线从一边到另一边。

接下来设计查询算法,必须控制在O(log(n))以内,所以容易想到二分,树状数组,线段树。又因为二分时需要保证有序,能查询到所在位置,还要增删元素,复杂度系数过大导致容易超时,真不是因为我wa了才回头的,线段树较费时,所以选择树状数组。 

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(0); cin.tie(0), cout.tie(0)
#define lowbit(x) (x&(-x))
typedef long long ll;
const int MAXN=200010;

struct Line {//离散化,标记关键节点
    int x, tag, num;//x用来排序, tag入边为1,出边为-1, num表示是第几条线段。
    bool operator<(const Line &t) const{return x<t.x;}//重载小于号, 可以用cmp替代
}c[MAXN<<1];

int n, a[MAXN], b[MAXN], d[MAXN];


inline void update(int x, int f) {//树状数组区间更新,f只可能为1或-1
    for(int i=x; i<=MAXN; i+=lowbit(i))
        d[i] += f;
}

inline ll query(int x) {//统计区间[1, x]存在的元素个数
    ll ans = 0;
    for(int i=x; i; i-=lowbit(i))
        ans += d[i];
    return ans;
}

void solve()
{
    cin>>n;
    for(int i=1; i<=n; ++i) 
    {
        cin>>a[i]>>b[i];
        c[i]={a[i], 1, i};
        c[i+n]={b[i], -1, i};
    }
    n<<=1;//n变为2n
    sort(c+1, c+n+1);//升序排序,没有重载小于号可以写个cmp放sort里
    
    ll ans = 0, cur = 0;//cur表示入边的先后
    memset(d, 0, sizeof d);
    for(int i=1; i<=n; ++i)
    {
        if(c[i].tag==1) update(++cur, 1), a[c[i].num] = cur;//标记num是第cur条进去的边
        else {
            ans += query(a[c[i].num]);//统计cur小于等于它的个数(包含了自己)
            update(a[c[i].num], -1);//出边,将他的贡献从中减去
        }
    }
    //因为统计query时包括了自己,所以最后ans减去点的个数,n之前扩大了一倍,所以为n/2
    cout<<ans-n/2<<'\n';
}

signed main(void)
{
    ios;
    int t=1; cin>>t;
    while(t--) solve();
    return 0;
}

G:

算法:Dijkstra,最短路, 动态规划

题意明显为最短路问题,但有个自行车权值,floyd会超时,蒟蒻不会johnson全源最短路,因此选择二维的dijkstra算法

思路:

将原算法的 dis 与 vis 改为二维,其余基本一致

dis[i][j]表示 从原点,中途用过的最小系数为j的自行车,到i点的最小时间。

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(0); cin.tie(0), cout.tie(0)
#define endl '\n'
//#define int long long 懒人专用
typedef long long ll;
const ll inf = 0x7fffffff;
const int MAXN=1010, MAXM=1010;

//链式前向星
struct Edge {
    ll to, dis, next;//此处dis为边的权值 
}e[MAXM<<1];//正反建边开两倍空间

struct St {
    ll dis;//此处dis为权值 
    ll pos;//此处pos为fr 
    ll rate;//自行车系数
    bool operator<(const St &t) const{return dis>t.dis;}
};

int n, m, cnt;
bool vis[MAXN][MAXN];
//dis[i][j]表示从原点,中途用过的最小系数为j的自行车,到i点的最小时间
ll dis[MAXN][MAXN], head[MAXN], b[MAXN];
priority_queue<St> q;


inline void add_edge(int u, int v, int w)
{
    e[++cnt].dis = w;
    e[cnt].to = v;
    e[cnt].next = head[u];
    head[u] = cnt;
}

inline void dijkstra()//堆优化迪杰斯特拉
{
    dis[1][b[1]] = 0;
    q.push((St){0, 1, b[1]});
    while(!q.empty())
    {
        ll fr = q.top().pos, bike = q.top().rate;
        q.pop();
        if(vis[fr][bike]) continue;
        vis[fr][bike] = true;
        for(int i=head[fr]; i; i=e[i].next)
        {
            ll to = e[i].to, minbike = min(bike, b[to]);
            if(dis[to][minbike] > dis[fr][bike] + e[i].dis*bike)
            {
                dis[to][minbike] = dis[fr][bike] + e[i].dis*bike;
                if(!vis[to][minbike]) {
                    q.push((St){dis[to][minbike], to, minbike});
                }
            }
        }
    }
}

void solve()
{
    cin>>n>>m;
    memset(e, 0, sizeof e);
    memset(head, 0, sizeof head);
    for(int i=1; i<=1000; ++i)
        for(int j=1; j<=1000; ++j)
            dis[i][j] = inf*inf, vis[i][j] = 0;
    
    cnt = 0;
    for(int i=1; i<=m; ++i)
    {
        ll u, v, w; cin>>u>>v>>w;
        add_edge(u, v, w);
        add_edge(v, u, w);//无向图反向建边
    }
    
    for(int i=1; i<=n; ++i) cin>>b[i];
    
    dijkstra();
    
    ll ans = inf*inf;//inf为int上限,inf*inf接近longlong上限
    for(int i=1; i<=n; ++i)
        ans = min(ans, dis[n][b[i]]);
    cout<<ans<<endl;
}

signed main(void)
{
    ios;
    int t=1; cin>>t;
    while(t--) solve();
    return 0;
}

蒟蒻不易qvq,佬佬们点个赞再走吧Orz,感谢观看。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值