HDU 6865 - Kidnapper's Matching Problem
题意
给定(n,m,k(mleq n)),给定长度为(n)的数组({a}),长度为(m)的数组({b}),长度为(k)的集合({S})
定义(2_⊕^S)为({S})的任意子集的异或和(xor_sum)的可能结果所形成的新集合
定义两等长集合({x},{y})的(matches)函数
该函数会遍历判断(x_i xor y_i in 2_⊕^S)
如果对于所有的(i(ileq|x|,|y|)),(x_i xor y_i in 2_⊕^S)均成立,则(matches)返回(1),否则返回(0)
问下式的值
限制
(1leq Tleq 2cdot10^4)
(1leq nleq2cdot10^5, 1leq mleq min(n, 5cdot10^4), 1leq kleq100)
(0leq a_i,b_i,S_ilt2^{30})
(sum nleq1.2cdot10^6, sum mleq3cdot10^5, sum kleq 6cdot10^5)
思路
先求出({S})的线性基(bs)
(下面是官方题解的内容)
结论:假设(x,y)消去线性基(bs)中的位后得到的数分别位(x',y'),那么下列关系式成立
证明:
充分性:根据线性基的性质,(x⊕y)必然能由(x'⊕y')再异或上线性基(bs)内某些数得到,所以在(x'=y')时(x⊕yin 2_⊕^S)
必要性:反证,因为(x',y')不包含线性基(bs)中的位(对应的位被消去),所以(x'⊕y')也不会包含(bs)中的位。又因为(x' eq y'),所以(x'⊕y' eq0),则(x'⊕y')一定包含无法用(bs)来表示的位,故(x'⊕y' otin 2_⊕^S)
也确实只要有了这个结论,让({a})与({b})中所有元素全部消去线性基(bs)中的位后,本题便变成了一道快速匹配模板题
由于(ngeq m),且答案是根据([Sub(a) matches b])来计算的,故选择({a})作为主串,选择({b})作为模式串,套KMP即可
主要注意对于题目公式中的(i=1),实际上在KMP中主串光标应该是位于(i=m)位置才对
所以如果在主串第(p)个位置匹配成功,实际上应该加上(2^{p-m+1})
由于按照顺序来匹配,(2^n)通过递推即可,其余的注意下细节就可以了
代码
(826ms/2000ms)
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int n,m,k,a[200050],b[200050],S[111];
int Next[200050];
struct LinearBase
{
int v[35];
void init()
{
memset(v,0,sizeof v);
}
void insert(int x)
{
for(int i=30;i>=0;i--)
if(x>>i&1)
{
if(v[i])
x^=v[i];
else
{
v[i]=x;
break;
}
}
}
int solve(int x)
{
for(int i=30;i>=0;i--)
if(x>>i&1)
x^=v[i];
return x;
}
}bs;
void getNext()
{
Next[1]=0;
for(int i=2,j=0;i<=m;i++)
{
while(j>0&&b[j+1]!=b[i])
j=Next[j];
if(b[j+1]==b[i])
j++;
Next[i]=j;
}
}
void KMP()
{
int n2=1,ans=0;
for(int i=1,j=0;i<=n;i++)
{
while(j>0&&b[j+1]!=a[i])
j=Next[j];
if(b[j+1]==a[i])
j++;
if(j==m)
{
ans=(ans+n2)%mod;
j=Next[j];
}
if(i>=m)
n2=(2LL*n2)%mod;
}
printf("%d
",ans);
}
void solve()
{
scanf("%d%d%d",&n,&m,&k);
bs.init();
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=m;i++)
scanf("%d",&b[i]);
for(int i=1;i<=k;i++)
{
scanf("%d",&S[i]);
bs.insert(S[i]);
}
for(int i=1;i<=n;i++)
a[i]=bs.solve(a[i]);
for(int i=1;i<=m;i++)
b[i]=bs.solve(b[i]);
getNext();
KMP();
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
solve();
return 0;
}