Codeforces Round #502 (in memory of Leopoldo Taravilse, Div. 1 + Div. 2)D. The Wu

题目链接: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;
}

猜你喜欢

转载自blog.csdn.net/qq_36782366/article/details/81534953