Codeforces Round 889 (Div. 2) C1 - C2

发布时间 2023-07-30 16:07:10作者: 一棵木头罢辽

Problem - C1 - Codeforces

Problem - C2 - Codeforces

题意:

​ 有\(n\)个数字,可以选择任意两个位置\(i,j\)进行操作,使得\(a_i=a_i+a_j\)(也即是\(a_j\)加到\(a_i\)),输出任意操作方案使得数组最后是不降的。esay-version要求在50次操作内完成,hard-version要求在31次操作内完成。

hard-version和easy-version有共同之处,所以放在一起讲了。

思路:

​ 首先,我们可以大致分成三种情况讨论:全为正数全为负数,以及有正有负(在这里0不会对最终答案产生影响,可以放进任意一种情况里)

​ 比较容易看出,对于全正的,我们只需要从左到右\(a_{i}\)加到\(a_{i + 1}\)上,最后就一定能保证不降(每个数都等于它前一个数\(+k\)\(k\)大于等于\(0\),则每个数都大于等于其前一个数)

​ 相对应的,对于全负的情况,我们只需要从右到左\(a_i\)加到\(a_{i - 1}\)上,也可以保证不降。

​ 可以容易看出,上面两种处理最多只需要操作19次,同时满足了两个版本的要求。

​ 那么接着考虑有正有负。

​ 对于easy-version,可以对数组中正数和负数的个数进行统计,同时记录正数中的最大值和负数中的最小值,如果正数个数大于负数个数,那么我们只需要让正数的最大值进行自加,直到大于负数最小值的绝对值,然后再把它加到所有负数上,就可以让所有负数变成正数,然后继续上面全正的操作即可。如果负数个数大于正数个数则同理。

​ 上面方案中最坏的情况就是\(1,1,1,1,1,1,1,1,1,1,1,-20,-20,-20,-20,-20,-20,-20,-20,-20\),在这种情况下,\(1\)也只需自加\(5\)次就已经大于\(20\),加到每一个\(-20\)需要操作\(9\)次,进行全加操作\(19\)次,最终操作次数也只是\(5 + 9 + 19 = 33\),远远小于easy-version的操作上限,但却正好超过了hard-version的操作上限。

​ 思考更好的方法,观察可以发现,对于上面的情况,如果我们直接把\(-20\)加到\(1\)上,使数组变成全负,那么总共只需要\(11 + 19=30\)次操作,就可以达成要求,对于hard-version的上限\(31\),最多可以将大数加到别的数上\(12\)次(若数组大小为20),则总结一下,如果正数最大值大于等于负数最小值的绝对值,且负数个数小于等于\(12\),那么我们执行直接变成全正相加会是更优的,若负数最小值的绝对值大于等于正数最大值则同理。如果都不满足,再使用一开始的思路(自增)就可以了。

​ 事实上上面的极限个数还会随着数组大小变化,但这个方法只会更优,且一定满足条件,所以不用继续优化也可以。

void QAQ(){
    int n;
    cin >> n;
    int minn = INF, maxx = -INF;
    vector<int> num(n + 1);
    for(int i = 1; i <= n; i ++){
        cin >> num[i];
        maxx = max(maxx, num[i]);
        minn = min(minn, num[i]);
    }
    queue<pii> op;
    if(minn >= 0){//全正
        for(int i = 1; i < n; i ++){
            // cout << i + 1 << " " << i << endl;
            op.push({i + 1, i});
        }
    }
    else if(maxx <= 0){//全负
        for(int i = n; i > 1; i --){
            // cout << i - 1 << " " << i << endl;
            op.push({i - 1, i});
        }
    }
    else{//有正有负另外统计
        int a = 0, b = 0, aa = 0, bb = 0;
        int acnt = 0, bcnt = 0;
        for(int i = 1; i <= n; i ++){
            if(num[i] > 0){
                if(num[i] > a){
                    aa = i, a = num[i];
                }
                acnt ++;
            }
            else if(num[i] < 0){
                if(num[i] < b){
                    bb = i, b = num[i];
                }
                bcnt ++;
            }
        }
        //如果是easy-version直接看最后的else也可以
        if(abs(a) > abs(b) && bcnt <= 12){
            for(int i = 1; i <= n; i ++){
                if(num[i] < 0){
                    op.push({i, aa});
                }
            }
            for(int i = 1; i < n; i ++){
                op.push({i + 1, i});
            }
        }
        else if(abs(b) > abs(a) && acnt <= 12){
            for(int i = 1; i <= n; i ++){
                if(num[i] > 0){
                    op.push({i, bb});
                }
            }
            for(int i = n; i > 1; i --){
                // cout << i - 1 << " " << i << endl;
                op.push({i - 1, i});
            }
        }
        else{
            if(acnt > bcnt){
                //正数多
                while(abs(a) < abs(b)){
                    a += a;
                    op.push({aa, aa});
                }
                for(int i = 1; i <= n; i ++){
                    if(num[i] < 0){
                        op.push({i, aa});
                    }
                }
                for(int i = 1; i < n; i ++){
                    op.push({i + 1, i});
                }
            }
            else{
                while(abs(b) < abs(a)){
                    b += b;
                    op.push({bb, bb});
                }
                for(int i = 1; i <= n; i ++){
                    if(num[i] > 0){
                        op.push({i, bb});
                    }
                }
                for(int i = n; i > 1; i --){
                    // cout << i - 1 << " " << i << endl;
                    op.push({i - 1, i});
                }
            }
        }
    }
    cout << op.size() << endl;
    while(!op.empty()){
        int x, y;
        tie(x, y) = op.front();
        op.pop();
        cout << x << " " << y << endl;
    }
}