Codeforces Round #645 (Div. 2) F.Tasty Cookie(思维题/分类讨论)

题目

给你一个n(n<=2e5),一个A数组,一个B数组(1<=Ai,Bi<=1e12)

你可以对A数组进行两种操作,

①反转A数组,记为R操作

②对A数组做前缀和,即[A1,A2,...]->[A1,A1+A2,...],记为P操作

问A能否变成B,如果不能输出IMPOSSIBLE

否则,

若P操作超过2e5次,输出BIG,以及P操作的次数

P操作没超过2e5次,输出SMALL,R和P的操作总数,以及R和P的操作序列

思路来源

http://www.360doc.com/content/20/0604/21/13328254_916532267.shtml

qls代码

题解

考虑到一个长度为3的1 1 1数组,做前缀和n次,第二项是O(n)级的,第三项是O(n^2)级的,

这样大致1e6次操作就能到1e12了,所以n>3的可暴力

暴力的时候,发现a到b也不好做,

考虑如何b逆推回a,因为做完前缀和之后的数组肯定是增序的,这样就可以差分回去了

先检查a和b是否完全一致,a和b的逆序是否完全一致,一致直接返回

否则,如果b是严格增序(因为没有0)或降序的,说明经历了一次前缀和或前缀和加一次反转

那么如果是增序的,就差分一次,再判断

n=1显然可以特判,考虑n=2怎么做

 

n=2,也在每步之前,判一下a和b是否完全一致,a和b的逆序是否完全一致,一致直接返回,

否则,不妨设b[1]<b[2](大于先R一次),考虑其与min(a[1],a[2])和max(a[1],a[2])的关系,

若b[1]<min(a[1],a[2]),显然无法再通过差分得到

若b[1]=min(a[1],a[2]),说明已经对齐,此时b[2]如果能减去b[1]若干次得到max(a[1],a[2])就是Yes,否则No

若b[1]>min(a[1],a[2]),说明最小值还没达到,那就可以加速这个b[2]不断减b[1]的过程,得到(b[1],b[2]%b[1])后再考虑

实际实现的时候,考虑b[2]可能是b[1]的倍数,但是b[2]不该变成0(因为序列里没有0)

因此会给b[2]留一点,b[2]-=((b[2]-1)/b[1]*b[1]),这样b[2]就最少是1了

设t=b[2]减掉的b[1]的倍数,如果某一步t<=0,说明本次操作不可减/什么都没减,显然不合法

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define sci(a) scanf("%d",&(a))
int n;
ll a[N+5],b[N+5],cntp;
string res;
void out(bool x){
    if(!x)puts("IMPOSSIBLE");
    else{
        if(cntp>N){
            puts("BIG");
            printf("%lld\n",cntp);
        }
        else{
            puts("SMALL");
            reverse(res.begin(),res.end());
            printf("%d\n%s\n",(int)res.size(),res.c_str());
        }
    }
}
bool ok(){
    bool sm=1;
    rep(i,1,n){
        sm&=(a[i]==b[i]);
    }
    if(sm)return 1;
    sm=1;
    rep(i,1,n){
        sm&=(a[i]==b[n+1-i]);
    }
    if(sm){
        if(cntp<=N){
            res+='R';
        }
        return 1;
    }
    return 0;
}
bool solve2(){
    ll x=min(a[1],a[2]),y=max(a[1],a[2]);
    while(1){
        if(ok())return 1;
        if(b[1]<b[2]){
            ll t=0;
            if(b[1]<x){
                return 0;
            }
            else if(b[1]==x){//如果最小值已经对齐
                t=(b[2]-y)/b[1];//b[2]->y(令b2至少为y,尝试令最大值对齐)
            }
            else{
                t=(b[2]-1)/b[1];//b[2]->(b[2]%b[1])(且令b2至少为1)
            }
            if(t<=0){//已经不能进行这样的操作了
                return 0;
            }
            else{
                b[2]-=t*b[1];
                if((cntp+=t)<=N){
                    res+=string(t,'P');
                }
            }
        }
        else if(b[1]>b[2]){
            swap(b[1],b[2]);
            if(cntp<=N){
                res+='R';
            }
        }
        else{
            return 0;
        }
    }
}
bool solve3(){
    while(1){
        if(ok())return 1;
        bool u=1,d=1;//up down
        rep(i,1,n-1){
            u&=(b[i]<b[i+1]);
            d&=(b[i]>b[i+1]);
        }
        if(u){
            per(i,n,2){
                b[i]-=b[i-1];
            }
            if((++cntp)<=N){
                res+='P';
            }
        }
        else if(d){
            reverse(b+1,b+n+1);
            if(cntp<=N){
                res+='R';
            }
        }
        else{
            return 0;
        }
    }
}
int main(){
    sci(n);
    rep(i,1,n)scanf("%lld",&a[i]);
    rep(i,1,n)scanf("%lld",&b[i]);
    if(n==1)out(a[1]==b[1]);
    else if(n==2)out(solve2());
    else out(solve3());
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值