题意:
给你一个序列a,他有n+1个数,每个数的范围是ai >= 0 && a[i] <= n,同时任意两个数字都是不相同的,就是ai != aj (i!=j),然后让你构造一个序列b,这个序列的要求与a序列一样,最后要求构造的b是使 t = (a0 ^ b0) + (a1 ^ b1) + ...+ (an ^ bn)最大。
思路:
给你一个序列a,他有n+1个数,每个数的范围是ai >= 0 && a[i] <= n,同时任意两个数字都是不相同的,就是ai != aj (i!=j),然后让你构造一个序列b,这个序列的要求与a序列一样,最后要求构造的b是使 t = (a0 ^ b0) + (a1 ^ b1) + ...+ (an ^ bn)最大。
思路:
一开始想的太简单了,因为题目要求的是每个数字都只能用一次,一开始我的想法是根据n的奇偶,来单独处理,处理的时候也是根据每个ai的奇偶来处理的,结果wa了,现在来说下正确的解法吧,这个题目我用的正确解法有点贪心的意思,我们每次先找到一个最大的2^k的数字,这个数字要小于等于n,从它开始枚举,往两边扩,如果不能扩了就找到当前没有被找出来的最大的那个2^k继续扩,不怎么好解释,自己在纸上多写几个连续的二进制数,然后模拟下很容易懂的,这里面有一个很容易让人怀疑的地方就是可能大数的那个方向会有剩余,其实不用担心这个,不可能有剩余,因为如果以某一个数为中心往两侧扩的话,只要前面扩到头了,也就是到1了,那么后面一定是mark完的了,因为扩到1相当于自己*2了,也就是2^(k+1)了,要是没有看懂什么意思,那么就自己找几个连续的二进制数模拟下就行了。
#include<stdio.h> #include<string.h> #include<stack> #define N 110000 using namespace std; int mark[N]; int num[N] ,Ans[N]; int main () { int n ,i ,tmp; while(~scanf("%d" ,&n)) { for(i = 0 ;i <= n ;i ++) scanf("%d" ,&num[i]); stack<int>my_sk; tmp = 1; while(1) { my_sk.push(tmp); tmp <<= 1; if(tmp > n) break; } memset(mark ,0 ,sizeof(mark)); memset(Ans ,0 ,sizeof(Ans)); while(!my_sk.empty()) { int L = my_sk.top() - 1; int R = L + 1; my_sk.pop(); while(1) { if(L < 0 || R > n || mark[L] || mark[R]) break; mark[L] = mark[R] = 1; Ans[L] = R ,Ans[R] = L; L -- ,R ++; } } __int64 ans = 0; for(i = 0 ;i <= n ;i ++) ans += (__int64)(num[i] ^ Ans[num[i]]); printf("%I64d\n" ,ans); for(i = 0 ;i <= n ;i ++) if(i == n) printf("%d\n" ,Ans[num[i]]); else printf("%d " ,Ans[num[i]]); } return 0; }