AT Tokiomarine2020E O(rand)
AT Tokiomarine2020E O(rand)
很棒(又被吊锤)的一道容斥题
一些显然的结论
先简单的考虑一下S和T之间的大致关系
若\(S \nsubseteq T\) 显然无解
由于任意一种方案,所有选中的数的与为T
所以对于一个元素\(A_k\)可能出现在方案内,当且仅当\(S \subseteq A_k \subseteq T\)
因为\(T\)是所有元素的公共部分,相当于无效,可以直接删去
同时,剩下的元素每一位等价,可以离散 使得更加好看
离散后的问题可以简化为
给定一些数,要求从中选出不超过k个数,使得 Or为 \(2^k-1\), And 为0
换言之,就是选出的数对于每一位都同时有1和0
这么一说题干就有容斥的感觉了
Solve
设\(f(S)\)为恰好S位上的数合法
设\(g(S)\)为最多S位上的数合法
设全集为\(SS\)
题目所求即\(f(S)\)
显然有
\[g(S)=\sum_{T \subseteq S} C_{|S|}^{|T|} f(T)
\]
通过二项式反演得
\[f(s)=\sum_{T \subseteq T} (-1)^{|S|-|T|}*C_{|S|}^{|T|}*g(T)
\]
最多有\(S\)位合法实际就是最少有\(SS \oplus S\)这些位不合法
为了强制这些位不合法,我们所选取数的集合中必然所有数 and \(SS \oplus S\)的结果相同
于是 我们只需要将所有数 对于 \(TT \oplus S\)的与运算结果分类讨论即可
代码环节
#include<bits/stdc++.h>
using namespace std;
#define Mod(x) (x>=P)&&(x-=P)||(x<0)&&(x+=P)
#define rep(i,a,b) for(ll i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(ll i=a,i##end=b;i>=i##end;--i)
#define erep(i,a) for(ll i=hd[a];i;i=nxt[i])
typedef long long ll;
void Max(ll &x,ll y){(x<y)&&(x=y);}
void Min(ll &x,ll y){(x>y)&&(x=y);}
bool vio;
char IO;
ll rd(ll res=0){
bool f=0;
while(IO=getchar(),IO<48||IO>57)
f=IO=='-';
do res=(res<<1)+(res<<3)+(IO^48);
while(IO=getchar(),isdigit(IO));
return f?-res:res;
}
const ll M=105;
ll A[M],g[M],pc[1<<18],C[M][M],pw[M],D[M][M];
ll cnt[1<<18];
bool let;
int main(){
cerr<<(&vio-&let)/1024.0/1024<<endl;
ll n=rd(),K=rd(),S=rd(),T=rd();
if((S&T)!=S)return puts("0"),0;
rep(i,1,n){
ll t=rd();
if((t&S)==S&&(t|T)==T)A[++A[0]]=t^S;
}
T^=S;
rep(i,1,A[0]){
ll bas=1,s=0;
rep(j,0,18){
if(1<<j&A[i])s|=bas;
if(1<<j&T)bas*=2;
}
A[i]=s;
if(i==A[0])T=bas-1;
}
rep(i,1,T)pc[i]=pc[i>>1]+(i&1);
rep(i,0,A[0])rep(j,C[i][0]=1,i){
C[i][j]=C[i-1][j]+C[i-1][j-1];
D[i][j]=D[i][j-1]+C[i][j];
}
ll ans=0;
rep(i,0,T){
ll tmp=0;
rep(j,1,A[0]){
tmp-=D[cnt[A[j]&i]][min(cnt[A[j]&i],K)];
cnt[A[j]&i]++;
tmp+=D[cnt[A[j]&i]][min(cnt[A[j]&i],K)];
}
rep(j,1,A[0])cnt[A[j]&i]=0;
ans+=(pc[i]&1?-1:1)*tmp;
}
printf("%lld",ans);
return 0;
}

浙公网安备 33010602011771号