题解 codeforces 1457-D Xor-gun
【题意】
初始给定长度为 \(n\),非递减的序列 \(\{a_n\}\) 。你可以进行操作,每次操作将两个相邻的数字消除,换为它们的异或和。问最少需要多少步使得原序列不再非递减。
【分析】
由于 \(a_i\leq 10^9\) 且 \(2^{29}<10^9<2^{30}\)
故根据抽屉原理,当数量 \(n\geq 2\times 30+1=61\) 时,至少存在一个位数,使得以该位数为最高位的数字存在至少 \(3\) 个,且这 \(3\) 个数连续。
设上述的其中一组 \(3\) 个数在序列中位置分别为 \(a_{pos}, a_{pos+1}, a_{pos+2}\) 。
则 \(a_{pos+1}\otimes a_{pos+2}\) 的原最高位被异或消除,必定小于 \(a_{pos}\) ,则直接打破了非递减性。
因此,当 \(n\geq 61\) 时,答案为 \(1\) 。
当 \(n\leq 60\) 时,采用随机化的方式,取步数最少解。
当不存在该解时,直接输出 \(-1\) 。
【代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
typedef double db;
#define fi first
#define se second
const int MAXN=1e5+10;
int n, a[MAXN], b[MAXN];
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
clock_t start = clock();
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i];
if(n>60)
cout<<1;
else{
int ans=0;
do{
memcpy(b+1, a+1, sizeof(a[0])*n);
int len=n;
while(len>1){
for(int i=1;i<len;++i)
if(b[i]>b[i+1]){
ans=max(ans, len);
break;
}
--len;
int pos=rand()%len+1;
b[pos]^=b[pos+1];
for(int i=pos+1;i<=len;++i)
b[i]=b[i+1];
}
}while( clock()-start<=1980 );
if(ans) cout<<n-ans;
else cout<<-1;
}
cout.flush();
return 0;
}