「JOI 2018 Final」毒蛇越狱

题目

传送门

题解

这道题写了两天,终于学懂了

在机房大佬 \(\text{JZM}\) 的帮助下,我总算是拿下这道十分巧妙的题。

\(\text{cnt}_i\)\(i\) 的数量。

首先,如果只有一组数据,我们可以直接 \(\mathcal O(2^{\text{cnt}_?})\) 暴力算。

但是由于有 \(Q\) 组询问,所以直接暴力算是会超时的。

考虑怎么进行优化?

考虑一种询问 \(??00011\),如果我们求 \(1100011\) 的子集,我们发现似乎多算了一些情况,具体是哪些?

显然,我们多算了 \(1100001,1100010,1100000\) 的情况,这些是什么?是我们强制为 \(1\) 的那些位。

那么,我们直接对于这些情况进行容斥即可。

考虑将这种方法进行推广:

对于一种询问,我们将 \(?\) 看成 \(1\),然后求子集的贡献,然后对于强制为 \(1\) 的那些位进行容斥。

由于子集贡献可以直接 \(FWT\) 预处理(下面给出思路,若已经了解可以直接跳过),最后一个问题是如何确认容斥符号?

考虑我们先拿到询问 \(s=1100011\) 的所有子集和,对于我们有一位变成了 \(1\),我们要减去这样的贡献,也就是当 \(\text{bitcnt}_i\bmod 2=1\) 时,我们需要减去,那么容斥即 \(\text{ans}=\text{ans}+(-1)^{\text{bitcnt}_i}f'[i]\)

如何用 \(FWT\) 预处理子集贡献?

考虑或卷积的 \(DWT\) 变换,对于一个序列,我们分成前一半和后一半,递归前一半、后一半,然后将前一半加到后一半上,这就相当于将某个数最高位去掉,然后加到它原来的位置,而他在递归回来之后已经包含了前面所有的子集和,然后加到后面去,这就相当于算上了子集和。

对于这道题而言,我们输入 \(w[i]\),存到 \(f[i]\) 里面,然后将 \(f\) 进行 \(DWT\_or\) 的正变换,得到的 \(f'[s]=\sum_{i\subseteq s}w[i]\)

这样的复杂度是 \(\mathcal O(Q2^{\text{cnt}_1})\),但是如果 \(1\) 很多,也很慢,这个时候我们可以分类讨论一下,如果 \(?\)\(1\) 少,那么我们直接暴力,如果 \(1\) 更少,我们就用 这种方法,显然,\(\min\{\text{cnt}_?,\text{cnt}_1\}\le 10\),最极限的情况是 \(\mathcal O(Q2^{10})\),显然过不去。

那么我们考虑进一步优化。

考虑我们之前的方法,我们在 \(1\) 最少的时候考虑容斥 \(1\),那么我们能否在 \(0\) 最少的时候对 \(0\) 进行容斥呢?

如何进行容斥?我们在容斥 \(1\) 的时候,相当于将 \(0\) 固定了,那么,我们在对 \(0\) 进行容斥的时候,将 \(1\) 固定下来,也就可以进行容斥了。

如何将 \(1\) 固定下来,我们分析一种情况:\(??00011\)(似曾相识)

首先,我们将 \(?\) 看成 \(0\),即询问变成 \(0000011\),然后,我们求一个这个东西:

\[g[s]=\sum_{s\subseteq i} w[i] \]

其中 \(s\) 是我们的询问的字符串。

上式即求包含 \(s=0000011\) 的所有状态的贡献和,不难看出,我们原来要求的 \(?\) 被看成了 \(0/1\),这是满足我们的要求的,并且,我们要求的 \(1\) 全都是 \(1\),最后多算的,其实就是我们将固定为 \(0\) 的一些数字错看成了 \(1\),显然,我们对于这些多算的进行容斥即可。

而容斥符号的确定,我们可以使用前面相同思路进行分析,具体想法可以看一下代码。

代码

#include<bits/stdc++.h>
using namespace std;

#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define erep(i,u) for(signed i=tail[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
typedef long long LL;
typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef unsigned uint;
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long

#ifdef _GLIBCXX_CSTDIO
#define cg (c=getchar())
template<class T>inline void qread(T& x){
    char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
template<class T>inline T qread(const T sample){
    T x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return f?-x:x;
}
#undef cg
template<class T>void fwrit(const T x){//just short,int and long long
    if(x<0)return (void)(putchar('-'),fwrit(-x));
    if(x>9)fwrit(x/10);
    putchar(x%10^48);
}
#endif
// template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
    inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
    return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}

inline void DWT_or(int a[],const int N,const int opt){
    for(int t=2;t<=N;t<<=1)
        for(int i=0,p=t>>1;i<N;i+=t)for(int j=i;j<i+p;++j)
            a[j+p]+=a[j]*opt;
}

inline void DWT_and(int a[],const int N,const int opt){
    for(int t=2;t<=N;t<<=1)
        for(int i=0,p=t>>1;i<N;i+=t)for(int j=i;j<i+p;++j)
            a[j]+=a[j+p]*opt;
}

const int MAXL=20;
const int MAXSIZE=1<<MAXL;

int bitcnt[MAXSIZE+5];
int l,sz,q,w[MAXSIZE+5];
int f[MAXSIZE+5];//w 的 or 变换
int g[MAXSIZE+5];//w 的 and 变换

inline void Init(){
    l=qread(1),q=qread(1);
    sz=1<<l;
    rep(i,1,sz)bitcnt[i]=bitcnt[i>>1]+(i&1);
    rep(i,0,sz-1)scanf("%1d",&w[i]),g[i]=f[i]=w[i];
    DWT_or(f,sz,1),DWT_and(g,sz,1);
}

inline void print(const int x){
    for(int i=l-1;i>=0;--i)if((x>>i)&1)putchar('1');
        else putchar('0');
    Endl;
}

#define sgn(x) ((x&1)?-1:1)
int x,y,z,ans;
char ch[MAXSIZE+5];
inline void solve(){
    scanf("%s",ch);
    x=y=z=0;
    rep(i,0,l-1){
        (x<<=1)|=(ch[i]=='0');
        (y<<=1)|=(ch[i]=='1');
        (z<<=1)|=(ch[i]=='?');
    }
    if(bitcnt[x]<=6){
        ans=g[y];
        for(int i=x;i;--i&=x)ans+=sgn(bitcnt[i])*g[i|y];
    }else if(bitcnt[y]<=6){
        int now=(sz-1)^x;
        ans=f[now];
        for(int i=y;i;--i&=y)ans+=sgn(bitcnt[i])*f[now-i];
    }else{
        ans=w[y];
        for(int i=z;i;--i&=z)ans+=w[i|y];
    }
    writc(ans,'\n');
}

signed main(){
    Init();
    while(q--)solve();
    return 0;
}
posted @ 2020-06-15 20:46  Arextre  阅读(258)  评论(0编辑  收藏  举报