【2022noip多校】异或
【题目描述】
对于一个元素介于 \([0,2^m)\) 且互不相同的长度为 \(n\) 的序列 \(a_1, a_2 ...,a_n\) ,定义它的特征序列为 \(p_0,p_1,...,p_{2^m-1}\) ,其中 \(p_i\) 表示使得 \(a_{p_i}\) 与 \(i\) 的异或值最大的下标。
形式化地,定义 $ p_i=\arg \max\limits_{1<=j<=n} a_j \oplus i $
给定一个特征序列 \(p\) ,求有多少个满足要求的原序列 \(a\) 可以得到这个特征序列。
答案对 \(10^9+7\) 取模
【样例】
【样例输入1】
6 3
1 1 2 2 3 4 5 6
【样例输出1】
4
【解析】
首先明确,合法的 \(p\) 中,必定出现完整的 \([1,n]\)
将\([0,2^m-1)\) 的二进制形式依次写出(取 \(m=3\) )
000 001 010 011 100 101 110 111
发现每次取一半,在第 \(k\) 次时,会使从高到底第 \(k\) 为发生 \(0,1\) 分割
所以考虑分治:
设状态 \([l,r]\) 表示在 \([l,r]\) 中,任意 \(a_{p_i} \oplus i\) 的前 \(dep\) 位相同, \(dep\) 表示分治深度。
其实这也代表了 \(a_{p_i}\) 前 \(dep\) 位相同,这个可以从之前的例子中看出。
接下来考虑下一层:
-
若集合 \(p_{l,..,mid}=p_{mid+1,..,r}\) 则表示下一位这两边也相同,则将方案数乘二,问题规模减半
-
若集合 \(p_{l,..,mid}!=p_{mid+1,..,r}\) 则代表下一位不同,且可以得出 \(a_{l,..,mid}\) 的 \(dep\) 位一定为 \(1\) , \(a_{mid+1,..,r}\) 的 \(dep\) 位一定为 \(0\),因为要保持最大且左区间的 \(dep\) 位均为 \(0\) ,右区间均为 \(1\) 所以有以上结论。之后这两种情况就独立了,乘法原理直接乘起来就可以了。
考虑无解,即集合 \(p_{l,..,mid}!=p_{mid+1,..,r}\) 但 \(p_{l,..,mid}\cap p_{mid+1,..,r}!=\varnothing\) 就无解。
【CODE】
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn = (1<<16)+33;
inline ll read_int(){
ll a=0;bool f=0;char g=getchar();
while(g<'0'||'9'<g) {if(g=='-') f=1;g=getchar();}
while('0'<=g&&g<='9') a=a*10+g-'0',g=getchar();
return f ? -a : a;
}
inline void write(ll a,bool b=1){
if(a<0) a=-a,putchar('-');
ll lin[30],top=0;
while(a) lin[++top]=a%10,a/=10;
if(top==0) lin[++top]=0;
while(top) putchar(lin[top--]+'0');
if(b) putchar('\n');
}
ll n,m,mod=1e9+7;
ll sj[maxn];
ll vis[maxn];
inline ll fz(ll l,ll r){
// cout<<l<<" "<<r<<endl;
if(l==r) return 1;
ll mid=(l+r)>>1;
int bt=0,xd=0;
set<int> a,b;
int f=0;
for(int i=l;i<=mid;i++) a.emplace(sj[i]);
for(int i=mid+1;i<=r;i++) b.emplace(sj[i]),f= (f||(a.find(sj[i])!=a.end()) ? 1 : 0);
if(f&&a!=b) return 0;
if(a==b) return (ll)2*fz(l,mid)%mod;
else return fz(l,mid)*fz(mid+1,r)%mod;
}
inline void read(){
n=read_int(),m=read_int();
for(int i=1;i<=(1<<m);i++) sj[i]=read_int(),vis[sj[i]]=1;
for(int i=1;i<=n;i++){
if(vis[i]==0) {write(0);return;}
}
write(fz(1,(1<<m)));
}
int main (){
// freopen(".out","w",stdout);
read();
// while(1) getchar();
}
【后记】
如有大佬知道之前那个合法的 \(p\) 中必有 \([i,n]\) 是如何证明的,请留言