Error Swap_arc198c分析与解答

C - Error Swap

操作在交换的基础上增加了加减1,首先可以发现,操作前后数组的所有数的和不变,因此如果初始时a数组元素的和不等于b数组元素的和,则a不可能变成b。

其实只要a数组元素的和等于b数组元素的和,a就一定能变成b,(其实还需要n>2,因为需要进行交换操作,请往下看),因为当对a_i和a_j (i<j)先交换再进行一次操作,可以让a_i减少1,让a_j增加1,若i>j,则先进行一次操作再交换,就也可以让a_i减少1,a_j增加1,而a数组元素的和与b数组元素的和相等的话,a中元素需要减少的量和需要增加的量是相等的,不断选出a_i和a_j,其中a_i是需要减小的元素,a_j是需要增大的元素,进行对a_i+1,对a_j-1的操作最终就可以让a_i等于b_i让a_j等于b_j。

但交换的操作如何实现呢?先看看有两个元素的情况,如果n=2,那么

a1,a2只能变成a2-1,a1+1,因为再变就又回到a1,a2,是无法实现交换操作的。n=2的情况我们特判。

n>2时,如果要交换a_i和a_j (i<j),可以借助第三个元素完成,如果i>1,可以借助他们左边的元素a_1完成:

用(a,b)(a<b)表示对a位置和b位置上的元素进行操作。

依次进行操作:(1,i) (1,j) (1,i)

如果i>1不满足,其实借助这两个元素右边的元素a_n也可以:
依次操作:(i,n) (j,n) (i,n)

如果i=1并且j=n,那能不能借助两个元素中间的元素对i,j进行交换?答案是可以,但是相较于以上两种交换的操作方式,这种交换的操作方式可能不那么直接想到,需要构造,可以按如下思路:

用a_i和a_j之间的a_p进行中转,i<p<j,最后一次操作对位置i,j进行,也就是最后一次操作是(i,j),那么最后一次操作前,i 位置上的值应该是a_i-1,j 位置上的值应该是a_j+1,如果将a_i向右换一次,向左换两次,就得到值a_i-1,同样将a_j向左换一次,向右换两次,得到值a_j+1,这一过程可以这样完成:
(i,j) (i,p) (p,j) (i,p)

加上最后的(i,j)的话,用p为中转交换(i,j)的操作序列是:
(i,j) (i,p) (p,j) (i,p) (i,j)

当i=1,j=n时,可以用2作为中转,写成:
(1,n) (1,2) (2,n) (1,2) (1,n)

现在分析一下总共需要的操作次数,让每个a_i和b_i的差值(差的绝对值)都最大,则总差值为nm,则需要进行mn/2次的a_i->a_i-1,a_j->a_j+1(记作“改变值”),如果让这mn/2次的改编自中,有尽量多的i=1和j=n,这样最后的总操作数会更大,那么在这mn/2次改变值中,最多有m/2次是对1和n改变值,所以总操作次数为:
mn/2*(交换需要3次+操作的1次)+(mn/2-m/2)*(交换需要5次+操作的1次),小于题目要求的31000。

#include<bits/stdc++.h>
using namespace std;
using ll=long long;

const ll maxn=100+5;
ll n;
ll a[maxn],b[maxn];
vector<pair<ll,ll>> op;

void opt(ll x,ll y){
    if(x>y) swap(x,y);
    op.push_back({x,y});
    ll t=a[x];
    a[x]=a[y]-1;
    a[y]=t+1;
}

void swp(ll x,ll y){
    if(y<x) swap(x,y);
    if(x>1) {
        opt(1,x);
        opt(1,y);
        opt(1,x);
    }else if(y<n){
        opt(n,x);
        opt(n,y);
        opt(n,x);
    }else {
        opt(1,n);
        opt(1,2);
        opt(2,n);
        opt(1,2);
        opt(1,n);
    }
}

int main()
{
    ios::sync_with_stdio(0);cin.tie(0);

    cin>>n;
    ll s1=0,s2=0;
    for(ll i=1;i<=n;i++) {cin>>a[i];s1+=a[i];}
    for(ll i=1;i<=n;i++) {cin>>b[i];s2+=b[i];}
    if(n==2){
        if(a[1]==b[1] && a[2]==b[2]) {
            cout<<"Yes"<<"\n";
            cout<<0<<"\n";
        }
        else if(a[2]-1==b[1] && a[1]+1==b[2]){
            cout<<"Yes"<<"\n";
            cout<<1<<"\n";
            cout<<1<<" "<<2<<"\n";
        }else {
            cout<<"No"<<"\n";
        }
    }else {
        if(s1!=s2) {
            cout<<"No"<<"\n";
        }
        else {
            cout<<"Yes"<<"\n";
            op.clear();
            vector<ll> p,q;
            for(ll i=1;i<=n;i++){
                ll d=a[i]-b[i];
                if(d>0) {
                    for(ll j=1;j<=d;j++) p.push_back(i);
                }else if(d<0){
                    d=-d;
                    for(ll j=1;j<=d;j++) q.push_back(i);
                }
            }

            //printf("p大小:%lld q大小:%lld\n",p.size(),q.size());

            for(ll i=0;i<p.size();i++){
                ll x=p[i],y=q[i];
                if(x<y) {
                    swp(x,y);
                    opt(x,y);
                }else {
                    opt(x,y);
                    swp(x,y);
                }
            }
            /*
            printf("Seq after operations:\n");
            for(ll i=1;i<=n;i++) cout<<a[i]<<" ";
            cout<<"\n";*/

            cout<<(ll)op.size()<<"\n";
            for(ll i=0;i<op.size();i++){
                cout<<op[i].first<<" "<<op[i].second<<"\n";
            }


        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值