专栏文章

arc187_a

AT_arc187_a题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mir3x2l6
此快照首次捕获于
2025/12/04 15:21
3 个月前
此快照最后确认于
2025/12/04 15:21
3 个月前
查看原文

A-Add and Swap

题意简述

有一个长度为 NN 的序列。
现在有一个操作:
1 n11~n-1 中选择一个位置 ii,让 i+1i+1 加上 KK,然后交换两个数的位置。
问在 5×1055\times 10^5 内能否让序列单调不减。如果可以,请输出操作序列。

Solution

我们先从小的地方来讨论一下:
  • n=2n=2 时:若 a1>a2a_1>a_2a2+K>a1a_2+K>a_1 无解。原因是重复操作某个位置偶数次,效果是让两个数同时加上偶数除以2个 KK,且相对大小仍然不变。
  • n=3n=3 时:由 n=2n=2 时的结论推广一下,发现可以这样做:让后面的操作200次,前面的操作100次,最后再对中间操作1次,可以保持正确。
  • 其它情况:从上述结论出发,我们可以从前往后搞一个长度为3的滑动窗口。先让后加进来的数放到窗口最前面,然后操作一次 n=3n=3 的操作即可。注意到到后面可能有操作完的情况不合法(一个例子就是设 AiA_i 已经操作结束,那么可能有 a1>a2a_1>a_2 的情况)。那么我们可以让第 ii 次操作乘上 ii 的一个偏移量即操作 200i200i100i100i 次就可以避开不合法的情况了。不难证明该操作次数一定小于 5×1055\times 10^5
CPP
//赛时代码可能写的有些冗余,请见谅
#include<bits/stdc++.h>
using namespace std;
vector<int>ans;
int n,k;
int a[100];
void ins(int i){
    ans.push_back(i);
    a[i+1]+=k;swap(a[i],a[i+1]);
}

void PRINT(){
    if(ans.size()>5e5)exit(-1);
    puts("Yes");
    printf("%d\n",ans.size());
    for(int i:ans)printf("%d ",i);
}
bool check(int i){
    if(a[i]<a[i-1]||a[i-1]<a[i-2])return false;
    return true;
}

void change(int i){
    ins(i-1),ins(i-2);
    for(int x=1;x<=(i-2)*200;++x)ins(i-1);
    for(int x=1;x<=(i-2)*150;++x)ins(i-2);
    ins(i-1);
}

void opt(int i){
    change(i);
}
int main(){
    cin>>n>>k;
    for(int i=1;i<=n;++i)cin>>a[i];
    if(n==2){
        if(a[1]>a[2]&&a[2]+k>a[1]){
            puts("No");
            return 0;
        }
        puts("Yes");
        if(a[1]<=a[2])puts("0");
        else cout<<"1\n"<<"1"<<endl;
        return 0;
    }
    for(int i=3;i<=n;++i)opt(i);
    PRINT();
    return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...