题目链接:http://codeforces.com/contest/1017/problem/D
题目大意:长度为n的01字符串。每一位都有他的价值a[i].
如果两个01字符串对应位置的值相同,那么就可以加上这一位对应的价值。
现在给出m个01字符串,和q个询问。
对于每一个询问,问给出的字符串与m个字符串之间,价值小于k的个数。
题目思路:(1≤n≤12, 1≤q,m≤5⋅10^5) (0≤k≤100)
首先我们观察他的数据范围,q和m都非常的大。如果对于每一个询问还有操作的话,肯定会超时。所以我们要考虑离线处理。
其次我们观察到n的值很小,即字符串最多有2^n的状态也就是四千多种,非常的小,这就是一个很好的切入口。
也就是给出的m个01字符串很多都是重复的,我们可以对他进行合并记录数量即可。
然后暴力枚举任何两种状态之间产生的价值存入数组即可。复杂度为4*10^3*4*10^3*12近似等于10^8,正好可以通过
#include <bits/stdc++.h>
using namespace std;
const int maxn=500000+20;
int n,m,q;
int a[maxn];
char s[maxn];
int change()
{
int ans=0;
int x=1;
for(int i=n-1;i>=0;i--)
{
if(s[i]=='1')
{
ans+=x;
}
x=x*2;
}
return ans;
}
int num1[maxn];
int dp[maxn][110];
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%s",s);
int aa=change();
num1[aa]++;
}
int len=(1<<n);
for(int i=0;i<len;i++)
{
for(int j=0;j<len;j++)
{
int tmp=0;
int ii=i;
int jj=j;
for(int k=n-1;k>=0;k--)
{
if((jj&1)==(ii&1))
{
tmp+=a[k];
}
jj=jj/2;
ii=ii/2;
}
if(tmp<=100)
dp[j][tmp]+=num1[i];
}
}
for(int i=0;i<q;i++)
{
int k;
scanf("%s%d",s,&k);
int aa=change();
int cnt=0;
for(int j=0;j<=k;j++)
{
cnt+=dp[aa][j];
}
printf("%d\n",cnt);
}
return 0;
}