【题目翻译】
给你两个长度都为n的正整数数组,让你把A数组通过两种操作变成B数组。 支持的两种操作: 1.R操作:把A数组翻转。 2.P操作:把A数组变成A数组的前缀和数组,即a[i]=∑a[j] (1<=j<=i)。 如果P操作的次数超过2*10^5,则不用输出操作序列(只需输出第二种操作个数就行),否则输出所有的操作序列【题解】
因为P操作是求前缀和操作,而且两个数组中每个数字最少是1。所以如果数组比较大的话,实际上加几次,最后一个数字就会很快变得特别大(超过10^12)。
(越长的话,P操作次数只会越小)。
题目作者作了张图,其中t是P操作的上限次数,可以感受一下。
这有什么用呢?
大家不要顺着题目的想法,每次都从A变换到B,实际上我们可以反过来想,怎么样才能从B变换到A呢。
考虑这里的P操作,因为是求A数组的前缀和,且A中每个数字都是大于0的,所以得到的B数组肯定是递增的。
并且,我们会发现,B数组也可以反过来求出A数组,a[i]=b[i]-b[i-1]
那么我们就有一个思路了,如果B数组不是严格递增的(相等的话a对应位置就是0了),那么它肯定不是由前缀和操作得来的。
所以,此时,如果B是严格递减的,那么还有希望,我们可以把它倒转一下,否则,如果不是严格递减也不是严格递增,并且和A还不相同。那么肯定是无解了。
如果是严格递增(减),并且和A还不一样,那么你有什么办法呢?肯定是做一次前缀和逆操作了。
所以,对于该题我们就有了一个完整的做法了:
1.如果B等于A的逆或者A,那么就找到合法的操作序列了,否则转第二步。
2.如果B是严格递增的,那么对B求一个前缀和的逆操作,否则转第三步。
3.如果B是严格递减的,那么对B数组求反,否则转第四步。
4.无解。
前缀和逆操作的个数就和上面那张图的每个t对应。时间复杂度为O(nans),且ans肯定是小于等于2t的,因为最坏情况就是每次求完逆前缀和,然后翻转一下。
上面那张图也能看出来,n2t的话,除了n=2的情况,都是可以在时间限制内出解的。
因此,我们对于n=2的情况,需要单独处理一下
对于n=2的情况,我们可以先把两个数组升序排一下(输出序列的时候考虑一下就行)
①然后,判断a[1]是否和b[1]相等,如果相等,则只需要看看b[2]减去多少次b[1]能够变成a[2],可以变成a[2]的话,就有解,输出即可。但是如果不能通过减去b[1]变成a[2],则无解。
②如果a[1]和b[1]不相等,则我们肯定是会一直对b数组进行逆前缀和操作的,但实际上我们可以加速这个过程,因为对b数组进行逆前缀和操作的最后结果就是,b数组变成
[b[1],b[2]%b[1]],因此,只要b[2]在一直减b[1]的过程中,不会变成a[2],那么我们就直接让它变成这个[b[1],b[2]%b[1]]的状态(累加需要进行的P操作次数),然后reverse一下(B又变成升序的了)
,再重复①过程。
这就是大体的思路,注意在操作过程中,我们是倒着做的,所以进行P操作,R操作,都是倒着来的。应该压入栈中,然后再一个一个弹出来输出答案。
(超过2E5的情况,可以在压入栈的时候,顺便查一下栈内元素个数是否超过界限。)
【代码】
#include<bits/stdc++.h>
#define ll long long
#define rei(x) scanf("%d",&x)
#define rel(x) scanf("%I64d",&x)
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
using namespace std;
const int N = 2e5;
int n;
ll a[N+10],b[N+10];
//B=A
bool equals(ll a[],ll b[]){
rep1(i,1,n) if (a[i]!=b[i]) return false;
return true;
}
//B=_reverse(A)
bool equals_reverse(ll a[],ll b[]){
rep1(i,1,n) if (a[i]!=b[n-i+1]) return false;
return true;
}
vector<char> ans;
ll cnt_p2 = 0;
void _add(ll cnt,char key){
if (key=='P'){
cnt_p2+=cnt;
}
if (cnt_p2>N) return;
for (int i = 1;i <= cnt;i++){
ans.push_back(key);
}
}
int main(){
//cout<<(1LL<<62)<<endl;
#ifdef LOCAL_DEFINE
freopen("D:\rush.txt","r",stdin);
#endif
ios::sync_with_stdio(0),cin.tie(0);
cin >> n;
rep1(i,1,n) cin >> a[i];
rep1(i,1,n) cin >> b[i];
if (n==1){
if (a[1]==b[1]){
cout<<"SMALL"<<endl;
cout<<0<<endl;
cout<<""<<endl;
}else{
cout<<"IMPOSSIBLE"<<endl;
}
}else if (n==2){
int flag1 = a[1]>a[2]?1:0;
if (a[1]>a[2]) swap(a[1],a[2]);
int flag2 = b[1]>b[2]?1:0;
if (b[1]>b[2]) swap(b[1],b[2]);
if (equals(a,b)){
if (flag1+flag2==2){
cout<<"SMALL"<<endl;
cout<<0<<endl;
cout<<""<<endl;
}
if(flag1+flag2==1){
cout<<"SMALL"<<endl;
cout<<1<<endl;
cout<<"R"<<endl;
}
if (flag1+flag2==0){
cout<<"SMALL"<<endl;
cout<<0<<endl;
cout<<""<<endl;
}
}else{
if (flag2) _add(1,'R');
ll cnt = 0;
while (1){
if (a[1]==b[1]){
if (a[2]>b[2]){
cout<<"IMPOSSIBLE"<<endl;
}else{
if ((b[2]-a[2])%b[1] == 0){
cnt += (b[2]-a[2])/b[1];
_add((b[2]-a[2])/b[1],'P');
if (flag1) _add(1,'R');
if (cnt>(long long)2e5){
cout<<"BIG"<<endl;
cout<<cnt<<endl;
}else{
cout<<"SMALL"<<endl;
cout<<(int)ans.size()<<endl;
while (!ans.empty()){
cout<<ans.back();
ans.pop_back();
}
}
}else{
cout<<"IMPOSSIBLE"<<endl;
}
}
break;
}else{
//a[1]!=b[1]
//b[1],b[2]%b[1]
ll tcnt = (b[2]-(b[2]%b[1]))/b[1];
cnt+=tcnt;
_add(tcnt,'P');
_add(1,'R');
b[2] = b[2]%b[1];
swap(b[1],b[2]);
if (b[1]==0){
cout<<"IMPOSSIBLE"<<endl;
break;
}
}
}
}
}else {
//n>3
ll cnt = 0;
while (1){
if (equals(a,b) || equals_reverse(a,b)){
if (equals_reverse(a,b) && !equals(a,b)) ans.push_back('R');
if (cnt>N){
cout<<"BIG"<<endl;
cout<<cnt<<endl;
}else{
cout<<"SMALL"<<endl;
cout<<(int)ans.size()<<endl;
while (!ans.empty()){
cout<<ans.back();
ans.pop_back();
}
}
break;
}else{
int cntz = 0,cntf = 0,cnt0 = 0;
rep1(i,2,n){
if (b[i]-b[i-1]==0){
cnt0++;
}else if (b[i]-b[i-1]>0){
cntz++;
}else cntf++;
}
if (cntz==n-1){ //ascn
_add(1,'P');
rep2(j,n,2) b[j] = b[j]-b[j-1];
cnt++;
}else if (cntf==n-1){//desc
reverse(b+1,b+1+n);
_add(1,'R');
}else {
cout<<"IMPOSSIBLE"<<endl;
break;
}
}
}
}
return 0;
}