题目链接:csoj | M. Minimal and Maximal XOR Sum (scnu.edu.cn)
解题思路:
最小值:每次操作的区间长度为2,即交换两个相邻数,每次异或2(10),故最小值肯定为2(10)或0(00),如果是偶排序最小值是0,奇排序最小值就是2。
最大值:
操作1:任选一个长度为 k 的区间,将其翻转,然后可以再利用k(k-1)/2次交换相邻两个数的操作再将这个区间翻转回去,相当于什么操作都没有做,而却多异或了一个 k 和 k(k-1)/2 个2
注意:当k为2^1即2时,操作1时无意义的,相邻两个数翻转再翻转,两次异或相同值2,sum不变。
当k为2^0即1时,l=r,故第一位肯定能变为1
故最大值( 位数为 n的二进制位数)只有第二位是有可能1有可能0(取决于最小值时该位是1还是0),其它位都可以直接变为1。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N], cnt, tmp[N];
void merge_sort(int l,int r)
{
if(l >= r) return;
int mid = (l + r) >> 1;
merge_sort(l, mid);
merge_sort(mid + 1, r);
int i = l, j = mid + 1, k = 0;
while(i <= mid && j <= r)
{
if(a[i] <= a[j]) tmp[k++] = a[i++];
else
{
cnt += (mid - i + 1);
tmp[k++] = a[j++];
}
}
while(i <= mid) tmp[k++] = a[i++];
while(j <= r) tmp[k++] = a[j++];
for(int i = l, j = 0; i <= r; i++, j++)
{
a[i] = tmp[j];
}
}
int main(){
int T;
scanf("%d", &T);
while(T--)
{
int n;
scanf("%d",&n);
for(int i = 0; i < n; i++) scanf("%d", &a[i]);
if (n == 1)//特判n=1
{
printf("0 1\n");
continue;
}
cnt = 0;//维护逆序对数量
int ans = 0;
int t = n;
while(t)//计算n的位数
{
t >>= 1;
ans++;
}
ans = (1 << ans) - 1;//计算ans位全是1的值
merge_sort(0, n - 1);//用归并排序求逆序对数,还可以用树状数组
if (cnt % 2 == 0) printf("0 %d\n", ans - 2);
else printf("2 %d\n", ans);;
}
return 0;
}