Codeforces Round #535 (Div. 3) 训练总结及题解

这场A一发7mins,Bwa2发20mins,C卡题了一会换题,D题5-7mins,E题40mins,F题假算法。
一开始A题5mins写完交的时候输出格式错了再读题耽误了2mins,B题写完13mins之后少考虑了两种情况,各RE了两发,20minsac,送了20+7分钟的罚时,C题没有细想跳开了,D题秒了,E2用的线段树T了,没有优化,F题没有耐心读。
总结:

  1. 读题需要读输出格式
  2. B题遇到有的出题人会在那种情况出fst点,总结下来就是枚举因子需要考虑因子相同的情况。
  3. C题可以用next_permutation写就不麻烦了。
  4. 开E2这种蹭时间线边缘的题时,要先开其他题。
  5. F题证明了自己对最小生成树俩个算法理解不够深。

题解:
A.
题意:给你两个区间让你O1输出两个数,使得这两个数都在任意个区间里,且第一个数在第一个区间里,第二个数在第二个区间里。
思路:取左端点最小的点,和未被取的最右边的点。
代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;


int main(){
    //IO;
    int q;cin>>q;
    while(q--){
        int l,r,l2,r2;cin>>l>>r>>l2>>r2;
        bool ok = 0;
        if(l<l2)swap(l,l2),swap(r,r2),ok=1;
        if(ok)cout<<l2<<' '<<r<<'
';
        else cout << r<<' '<<l2<<'
';
    }





    return 0;
}

B.
题意:给出两个数的所有因子,输出这两个数。
思路:取最大的因子为数1,用multiset删除掉所有数1的因子,输出剩下最大的因子。
代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;


int main(){
    //IO;
    int n;cin>>n;
    multiset<int>s;
    forn(i,n){
        int x;cin>>x;
        s.insert(x);
    }
    //cerr<<"!@"<<'
';
    int b = *s.rbegin();
    int m = sqrt(b);
    for(int i = 1;i<=m;i++)if(b%i==0){
        s.erase(s.find(i));
        if(i*i!=b)s.erase(s.find(b/i));
    }
    cout << *s.rbegin() <<' '<<b<<'
';

    return 0;
}

C.
题意:长1e5的字符串每个字符只会是RBG,求最少修改字符使得相同字符的位之差都为3。
思路:6个排列枚举出最小的,然后循环输出。
代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;

string a[6]={"RGB","RBG","BGR","BRG","GRB","GBR"};

int main(){
    IO;
    int n;cin>>n;
    string s;cin>>s;
    int ans = inf,pos = 6;
    string t="BGR",res;
    do{
        //cerr<<t<<'
';
        int cnt = 0;
        forn(i,n) if(s[i]!=t[i%3]) cnt++;
        if(ans>cnt) ans = cnt,res = t;
    }while(next_permutation(t.begin(),t.end()));
    cout << ans <<'
';
    forn(i,n) cout<<res[i%3];
    return 0;
}

D.
题意:长1e5的字符串每个字符只会是RBG,求最少修改字符使得相邻位没有相同字符。
思路:贪心按顺序修改相同的第二个字符。
代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;

int a[]={'R','G','B'};

int main(){
    //IO;
    int n;cin>>n;
    string s;cin>>s;
    int ans = 0;
    for1(i,n-1){
        if(s[i]==s[i-1]){
            ans++;
            if(i+1<n&&s[i]=='R'&&s[i+1]=='B') s[i] = 'G';
            else if(i+1<n&&s[i]=='B'&&s[i+1]=='R') s[i] = 'G';
            else if(i+1<n&&s[i]=='G'&&s[i+1]=='B') s[i] = 'R';
            else if(i+1<n&&s[i]=='B'&&s[i+1]=='G') s[i] = 'R';
            else if(i+1<n&&s[i]=='R'&&s[i+1]=='G') s[i] = 'B';
            else if(i+1<n&&s[i]=='G'&&s[i+1]=='R') s[i] = 'B';
            else if(s[i]=='R') s[i] = 'B';
            else if(s[i]=='B') s[i] = 'R';
            else s[i] = 'R';
        }
    }
    cout << ans<<'
';
    cout <<s<<'
';




    return 0;
}

E.
题意:1e5个数,300个区间操作,每次操作可以使这个区间所有值-1。每个操作可选可不选,问如何选择使得最终区间最大值-最小值最大。
思路:很容易想到枚举1e5个数为最大值,暴力用线段树区间修改非此点的区间,但是卡常了。可以这样优化。先将所有区间操作完,从1-n枚举每个最大值的点,如果有区间的左端点等于当前枚举的点,让该区间+1,如果有区间的右端点等于上一个枚举的点,让该区间-1。如此只用修改2m个区间。复杂度Omlogn,貌似不用线段树Omn也可以。当然建树需要O(nlogn)读入需要O(n),所以总复杂度O(n*logn)
代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int maxn = 1e5+5;
int a[maxn];
class segment_tree{
    public:
    #define nd node[now]
    #define ndl node[now<<1]
    #define ndr node[now<<1|1]
    struct segment_node{
        int l,r,v,flag;
        void update(int x){
            flag += x,v += x;
        }
    }node[maxn<<2];
    void pushup(int now){
        nd.v = min(ndl.v,ndr.v);
    }
    void pushdown(int now){
        if(nd.flag!=0){
            ndl.update(nd.flag);
            ndr.update(nd.flag);
            nd.flag = 0;
        }
    }
    void build(int l,int r,int now=1){
        nd={l,r,0,0};
        if(l==r){
            nd.v = a[l];
            return;
        }
        build(l,l+r>>1,now<<1);
        build((l+r>>1)+1,r,now<<1|1);
        pushup(now);
    }
    void update(int l,int r,int v,int now=1){
        if(l<=nd.l&&r>=nd.r){
            nd.update(v);
            return;
        }
        pushdown(now);
        if(l<=ndl.r)update(l,r,v,now<<1);
        if(r>=ndr.l)update(l,r,v,now<<1|1);
        pushup(now);
    }
}tree;

int main(){
    IO;
    int n,m;cin>>n>>m;
    for1(i,n) cin>>a[i];vector<pair<int,int> >b(m);
    tree.build(1,n);
    vector<int>l[n+1],r[n+1];
    forn(i,m){
        cin>>b[i].first>>b[i].second;
        tree.update(b[i].first,b[i].second,-1);
        l[b[i].first].push_back(b[i].second);
        r[b[i].second].push_back(b[i].first);
    }
    int ans = -inf,pos = 0;
    for1(i,n){
        for(auto x:l[i]) tree.update(i,x,1);
        for(auto x:r[i-1]) tree.update(x,i-1,-1);
        //for1(j,n<<2) cerr<<tree.node[j].v<<' ';
        //cerr<<'
';
        if(ans<a[i]-tree.node[1].v) ans = a[i]-tree.node[1].v,pos = i; 
    }
    cout << ans <<'
';
    vector<int>res;
    forn(j,m)if(pos<b[j].first||pos>b[j].second) res.push_back(j+1);
    cout<<res.size()<<'
';
    if(res.empty())cout<<'
';
    for(auto x:res) cout<<x<<' ';


    return 0;
}

F.
题意:n个点m条有边权的边的无向图。每次修改可以使得可以使一条边的边权+1,求最少修改多少次使得最小生成树唯一。
思路:不能用Prim,用Kruskal把权值相同的边所连的点未联通时先存起来,然后处理,如果此时有点边所连的点联通了,代表此边多余,ans++。
代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const int maxn = 2e5+5;
map<int,vector<pair<int,int> > >e;
int par[maxn],rankk[maxn];int n,m;

void init(){
    for1(i,n) par[i] = i,rankk[i] = 0;
}
int find(int x){
    return x==par[x]?x:par[x] = find(par[x]);
}
void unit(int x,int y){
    x = find(x),y = find(y);
    if(x==y)return;
    if(rankk[x]<rankk[y]) swap(x,y);
    else if(rankk[x]==rankk[y]) rankk[x]++;
    par[y] = x;
}

int main(){
    IO;
    cin>>n>>m;
    forn(i,m){
        int x,y,z;cin>>x>>y>>z;
        e[z].push_back({x,y});
    }
    init();
    int ans = 0;
    for(auto x:e){
        vector<pair<int,int> >a;
        for(auto u:x.second){
           // cerr<<"@!#!@##!@"<<'
';
            if(find(u.first)!=find(u.second)) a.push_back(u);
        }
        for(auto u:a){
            //cerr<<u.first<<' '<<u.second<<'
';
            if(find(u.first)!=find(u.second)) unit(u.first,u.second);
            else ans++;
        }
    }
    cout << ans <<'
';
    return 0;
}
人一我百,人十我万。
原文地址:https://www.cnblogs.com/AlexPanda/p/12520318.html