• 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;
    }
    
    人一我百,人十我万。
  • 相关阅读:
    2009年职工象棋比赛 第七轮 五七炮对屏风马
    2009年职工象棋比赛 第一轮 顺炮直车对横车
    给文件起个好名字是多么的重要!
    职工象棋比赛 第十一轮 仙人指路
    2009年职工象棋比赛 第八轮 顺炮横车对直车
    2009年职工象棋比赛 第三轮 中炮对反宫马
    2009年职工象棋比赛 第二轮 中炮对单提马
    终于记住了这两个词:devour、devout
    2009年职工象棋比赛 第四轮 顺炮直车对横车
    2009年职工象棋比赛 第五轮 中炮对右三步虎
  • 原文地址:https://www.cnblogs.com/AlexPanda/p/12520318.html
Copyright © 2020-2023  润新知