- 给定一个(0sim 2^n-1)的排列,求有多少个区间,满足在任意交换原序列中两个不同的数之后这个区间的异或和可以为(2^n-1)。
- (nle20)
不合法的判定条件
这种题目直接判合法显然不太容易,因此我们考虑什么样的情况不合法。
注意到一个极其重要的性质就是这道题中的数包含了完整的值域。
因此,假设一个不合法区间原本的异或和为(S),那么对于其中的任意一个数(x),都需要满足(Soplus xoplus(2^n-1))在这个区间中。
于是,我们认为异或和为(Soplus(2^n-1))的两个数能够配对。
显然,如果区间长度为奇数肯定会有至少一个数无法达成配对。
如果区间长度模(4)余(2),相当于有奇数个配对,每个配对异或和为(Soplus(2^n-1)),它们的总异或和仍旧是(Soplus(2^n-1)),显然不等于(S),因此不存在。
于是区间长度必然是(4)的倍数,此时有偶数个配对,那么它们的总异或(S)就等于(0),因此配对的条件就是异或和为(2^n-1),与具体是什么区间根本没有关系。
综上,不合法的判定条件就是区间长度是(4)的倍数且区间中的数能够实现配对。
哈希
我们可以给能够配对的两个数随机同一个权值,那么区间中的数能够实现配对可以认为就是要满足区间异或和为(0)。
给哈希数组做个前缀异或和,就是要求有多少对(lle r)满足(l-1equiv r(mod 4))且(H_{l-1}=H_r),直接用(map)数组维护即可。
代码:(O(2^nn))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 20
#define ull unsigned long long
#define Rd() (1ull*rand()*rand()*rand()*rand()+1)
using namespace std;
int n;struct Hash
{
ull x,y;I Hash(Con ull& a=0,Con ull& b=0):x(a),y(b){}
I Hash operator ^ (Con Hash& o) Con {return Hash(x^o.x,y^o.y);}
I bool operator < (Con Hash& o) Con {return x^o.x?x<o.x:y<o.y;}
}a[1<<N],h[1<<N];map<Hash,int> S[4];
namespace FastIO
{
#define FS 100000
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
char oc,FI[FS],*FA=FI,*FB=FI;
Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
int main()
{
srand(469762049);if(read(n),n==1) return puts("2"),0;//特判n=1,由于必须交换,单独的1是不合法的
RI i,x,l;for(l=1<<n,i=1;i<=l;++i) read(x),a[i]=h[x^(l-1)].x?h[x^(l-1)]:(h[x]=Hash(Rd(),Rd()));//给能配对的两个数随机同一个权值
ull ans=0;for(S[0][Hash()]=i=1;i<=l;++i) ans+=S[i%4][a[i]=a[i]^a[i-1]]++;return printf("%llu
",1LL*l*(l+1)/2-ans),0;//利用map求解非法方案数
}