[hdu-6865]Kidnapper's Matching Problem 线性基+KMP 2020多校8

【题目链接】http://acm.hdu.edu.cn/showproblem.php?pid=6865

【题意】

给出两个数组A B 和集合S,分别有n,m,k个数,n (1n210^5), m (1mmin(n,510^4)) and k (1k100),

求表达式

 

 

从n中取长度为m的连续一段,每个数字与对应m中的数异或  值如果都在 S集合的异或张成中(张成:一个集合中的数    其异或和的所有可能的结果组成的集合),则matched为1,否则为0.

【题解】

先求出S集合的线性基,然后对A,B集合中的数,消去对应线性基中的位。

则剩下的数如果想满足异或后在S集合中,必须相等。因为余下的位已经无法用线性基表示。

然后就变成了找A串中的B串,用KMP处理

官方证明:

 

 【AC代码】

#include <bits/stdc++.h>
using namespace std;
int const maxn=2e5+7,mod=1e9+7;
const int MAXL = 29,maxn2=5e4+7;
int n,m,k,nex[maxn2];
bool res[maxn];
long long a[maxn],b[maxn2],s[maxn2];
struct LinearBasis{
    long long aa[MAXL+1];
    LinearBasis(){
        std::fill(aa, aa + MAXL + 1, 0);
    }

    void insert(long long t){
        for (int j = MAXL; j >= 0; j--){
            if (!(t & (1ll << j))) continue;
            if (aa[j]) t ^= aa[j];
            else
            {
                for (int k = 0; k < j; k++) if (t & (1ll << k)) t ^= aa[k];
                for (int k = j + 1; k <= MAXL; k++) if (aa[k] & (1ll << j)) aa[k] ^= t;
                aa[j] = t;
                return;
            }
        }
    }

    void build(long long *x, int len){
        std::fill(aa, aa + MAXL + 1, 0);
        for (int i = 1; i <= len; i++){
            insert(x[i]);
        }
    }
}ji;//线性基模板
int main(){
    int t;
    scanf("%d",&t);

    for(int q=1;q<=t;q++){
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        for(int i=1;i<=m;i++){
            scanf("%lld",&b[i]);
        }
        for(int i=1;i<=k;i++){
            scanf("%lld",&s[i]);
        }
        ji.build(s,k);
        //对每一位消去线性基中的位
        for(int i=1;i<=n;i++){
            for(int j=MAXL;j>=0;j--)if(a[i]>>j&1)a[i]^=ji.aa[j];
        }
        for(int i=1;i<=m;i++){
            for(int j=MAXL;j>=0;j--)if(b[i]>>j&1)b[i]^=ji.aa[j];
        }
        long long ans=0;
        //KMP模板
        nex[1]=0;
        for(int i=2,j=0;i<=m;i++){
            while(j&&b[j+1]!=b[i])j=nex[j];//如果前缀匹配不上就一直往前跳到能匹配的前缀,要比只条一次快
            if(b[j+1]==b[i])j++;
            nex[i]=j;
        }
        memset(res,0,n);
        for(int i=1,j=0;i<=n;i++){
            while(j&&b[j+1]!=a[i])j=nex[j];
            if(b[j+1]==a[i])j++;
            if(j==m){
                res[i-m]=1;
                j=nex[j];
            }
        }
        for(int i=n-m;i>=0;i--)ans=(ans*2+res[i])%mod;
        printf("%lld\n",ans);
    }
}

 

posted @ 2020-08-15 10:44  conver^_^  阅读(212)  评论(0编辑  收藏  举报