cf1017d 状压预处理

该博客介绍了如何利用状态压缩预处理方法处理一类字符串问题。题目中涉及n个长度为n的01串,一个包含m个元素的集合,以及q次查询。每次查询给出一个01串t和整数k,要求找出集合中有多少个元素与t的'Wu'值小于等于k。'Wu'值定义为两个01串对应位置相同位的权值之和。博客通过样例解释了计算过程和答案得出的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给你n m q,表示在这一组数据中所有的01串长度均为n,然后给你一个含有m个元素的multiset,之后有q次询问。每次询问会给你一个01串t和一个给定常数k,让你输出串t和multiset里面多少个元素的“Wu”值不超过k。对于“Wu”值的定义:如果两个01串s和t在位置i上满足s[i]==t[i],那么加上w[i],处理完s和t的所有n位之后的结果即为这两个01串的“Wu”值。

样例解释:第1行为n,m,q;第2行为w[1]...w[n],表示01串第i位的权值为w[i];之后m行为multiset里的m个01串;之后q行为q次询问。以样例1中的第一次询问为例:t=00,k=20。与multiset中的每个元素比较(假设第i个元素为m[i]):

m[1]:01,与00相同的是第一位,所以权值s=w[1]=40;

m[2]:01,与00相同的是第一位,所以权值s=w[1]=40;

m[3]:10,与00相同的是第二位,所以权值s=w[2]=20;

m[4]:11,没有某一位与00相同,所以权值s=0;

而上述四种比较结果中s<=20的是后两种,所以输出2。之后的询问以此类推。
 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>

using namespace std;

int n,m,q;
int w[15];

int cnt[5005];
char tp[15];

struct node
{
    int val,num;
    friend bool operator<(node a,node b)
    {
        if(a.val==b.val)
            return a.num<b.num;
        else
            return a.val<b.val;
    }
}itm[5005];

bool cmp(node a,node b)
{
    return a.val<b.val;
}

int ansn[4105][4105];
int main() {

    while(~scanf("%d%d%d",&n,&m,&q))
    {
        memset(cnt,0,sizeof(cnt));

        for(int i=0;i<n;i++)
        {
            scanf("%d",&w[i]);
        }

        while(m--)
        {
            scanf("%s",tp);
            int cur=0;
            for(int i=0;i<n;i++)
            {
                 cur+=(1<<i)*(tp[i]-'0');
            }
            cnt[cur]++;
        }

        int len=(1<<n);
        for(int i=0;i<len;i++)
        {
            int val=0;
            for(int j=0;j<n;j++)
            {
                if(((i>>j)&1)!=0)
                {
                    val+=w[j];
                }
            }
            itm[i].val=val;itm[i].num=i;
        }

        sort(itm,itm+len);

        for(int i=0;i<len;i++)
        {
            for(int j=0;j<len;j++)
            {
                int cur=i^itm[j].num;
                cur=cur^(len-1);
                ansn[i][j]=cnt[cur];
                if(j>0)
                    ansn[i][j]+=ansn[i][j-1];
            }
        }

        while(q--)
        {
            int k;
            scanf("%s",tp);
            scanf("%d",&k);

            int cur=0;
            for(int i=0;i<n;i++)
            {
                cur+=(1<<i)*(tp[i]-'0');
            }

            node nxt;nxt.val=k;nxt.num=1e9+7;
            int id=upper_bound(itm,itm+len,nxt)-itm;
            if(id==0)
                printf("0\n");
            else
            {
                printf("%d\n",ansn[cur][id-1]);
            }
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值