思维题
这题有个小陷阱,比赛时掉进去了。过了pretest但是没能Accept
首先,最小的次数肯定就是1
既然是异或,那么我们很容易就会按照位进行思考。
我们想想,如果最高位为i的数字,总数超过3个。那么我们是不是一定能在1此操作中干掉这个数组。能的,对后两个进行异或,然后与前面的一个比较,一定小于前面的一个。
这是关键,因为通过这个方法,我们排除了大量的数据。
也就是说,经过这一步的筛选,最终剩下来的最多也只有64个数。
这就允许我们使用一些相对暴力的算法去解决了。
下面有一个结论:
如果我们操作了k此,那么这k次一定都是合并一个连续的区间。
为什么这样说呢?
假设k == 4 数组:
[a1,a2,a3,a4,a5,a6,a7]
如果我们将其分开a2,a3合并得num1 a6,a7合并得num2
num1 > num2 发现递减!!!
但是哈,注意了如果只有num1>num2的话
那么num1<=a4<=a5
则a5>num2
既然如此,我们只要枚举开始的索引,和异或的数量。然后向两边比较就可以了。
我很兴奋,我觉得自己找到了真相!!
但是我被fst了!!!!!!悲剧!悲剧!
我们上面的那个结论其实并不正确。
观看证明。如果合并的不是a2,a3 和 a6,a7而是a2,a3和a4,a5呢
情况大不相同。在种种情况下,因为没有中间元素所以他们是可行的。是有可能性成功的!
我们需要在枚举一层。。。。。。
惨痛的教训。
冷静的分析,清晰的头脑,加上大局观。这题我觉得很好。
#include<iostream> #include<algorithm> #include<cstring> #include<vector> using namespace std; const int max_n = 1e5+100; int a[max_n]; int b[max_n]; int c[max_n]; vector<int> cct[100]; int n; int yh(int l,int r){ return c[l]^c[r+1]; } int get(int num){ int ans = 0; while (num){ ++ans; num>>=1; }return ans; } int main(){ scanf("%d",&n); for (int i=1;i<=n;++i)scanf("%d",&a[i]); if (n==2){ printf("-1\n"); return 0; } for (int i=n;i>=1;--i)c[i] = c[i+1]^a[i]; for (int i=1;i<n;++i){ int tmp = a[i]^a[i+1]; if (i!=1&&tmp<a[i-1]){ printf("1\n"); return 0; } if (i+2<=n&&tmp>a[i+2]){ printf("1\n"); return 0; } } for (int i=1;i<=n;++i){ b[i]=get(a[i]); cct[b[i]].push_back(i); } for (int i=1;i<=32;++i){ if (cct[i].size()>=3){ printf("1\n"); return 0; } } int res = 1e5+100; bool f = true; for (int i=1;i<n;++i){ if (!f)break; for (int j=1;j<=n;++j){ int l = j;int r = j+i; if (r>n)break; int tmp = yh(l,r); if (l!=1&&tmp<a[l-1]){ res = i; f = false; break; } if (r!=n&&tmp>a[r+1]){ res = i; f = false; break; } if (r==n)continue; for (int k = l+1;k+1<=r;++k){ if (yh(l,k)>yh(k+1,r+1)){ res = i; f=false; break; } } if (!f)break; } } if (res==1e5+100)printf("-1\n"); else printf("%d\n",res); }