The Wu CodeForces - 1017D(状态压缩)

The Wu CodeForces - 1017D

Childan is making up a legendary story and trying to sell his forgery — a necklace with a strong sense of “Wu” to the Kasouras. But Mr. Kasoura is challenging the truth of Childan’s story. So he is going to ask a few questions about Childan’s so-called “personal treasure” necklace.

This “personal treasure” is a multiset S
of m

“01-strings”.

A “01-string” is a string that contains n
characters “0” and “1”. For example, if n=4, strings “0110”, “0000”, and “1110” are “01-strings”, but “00110” (there are 5 characters, not 4

) and “zero” (unallowed characters) are not.

Note that the multiset S

can contain equal elements.

Frequently, Mr. Kasoura will provide a “01-string” t
and ask Childan how many strings s are in the multiset S such that the “Wu” value of the pair (s,t) is not greater than k

.

Mrs. Kasoura and Mr. Kasoura think that if si=ti
(1≤i≤n) then the “Wu” value of the character pair equals to wi, otherwise 0. The “Wu” value of the “01-string” pair is the sum of the “Wu” values of every character pair. Note that the length of every “01-string” is equal to n

.

For example, if w=[4,5,3,6]
, “Wu” of (“1001”, “1100”) is 7 because these strings have equal characters only on the first and third positions, so w1+w3=4+3=7

.

You need to help Childan to answer Mr. Kasoura’s queries. That is to find the number of strings in the multiset S
such that the “Wu” value of the pair is not greater than k

.

Input

The first line contains three integers n

, m, and q (1≤n≤12, 1≤q,m≤5⋅105) — the length of the “01-strings”, the size of the multiset S

, and the number of queries.

The second line contains n
integers w1,w2,…,wn (0≤wi≤100) — the value of the i

-th caracter.

Each of the next m
lines contains the “01-string” s of length n — the string in the multiset S

.

Each of the next q
lines contains the “01-string” t of length n and integer k (0≤k≤100

) — the query.

Output

For each query, print the answer for this query.

Examples
Input

2 4 5
40 20
01
01
10
11
00 20
00 40
11 20
11 40
11 60

Output

2
4
2
3
4

Input

1 2 4
100
0
1
0 0
0 100
1 0
1 100

Output

1
2
1
2

Note

In the first example, we can get:

"Wu" of ("01", "00") is 40

.

“Wu” of (“10”, “00”) is 20

.

“Wu” of (“11”, “00”) is 0

.

“Wu” of (“01”, “11”) is 20

.

“Wu” of (“10”, “11”) is 40

.

“Wu” of (“11”, “11”) is 60

.

In the first query, pairs (“11”, “00”) and (“10”, “00”) satisfy the condition since their “Wu” is not greater than 20

.

In the second query, all strings satisfy the condition.

In the third query, pairs (“01”, “11”) and (“01”, “11”) satisfy the condition. Note that since there are two “01” strings in the multiset, the answer is 2
, not 1

.

In the fourth query, since k

was increased, pair (“10”, “11”) satisfies the condition too.

In the fifth query, since k
was increased, pair (“11”, “11”) satisfies the condition too.

题意:

给定n,m,q

n代表每个二进制串的长度其中 1 n 12 ,m代表m个二进制串,q表示接下来q个询问

然后给一个w序列,共有n个即 w 1 , w 2 , w n 表示二进制每个位的值

接下来q个询问每次给一个二进制串t和一个数k

这个二进制串t将和所给的每个二进制串进行比较,相同的位就加上这个位的值,最终和每个二进制串比较都可以得出一个值,问值小于等于k的串有多少个

分析:

根据数据量我们发现不能枚举一个一个比,询问和串的个数都是5e5,相乘是10e10

但是我们却发现n的长度非常小,12,因此想到二进制状态压缩来枚举状态,12的话总共就有4096中状态

首先用一个cnt[i]数组,记录状态i的二进制串的个数

然后开始枚举状态,求出两两状态比较得到的值,可以两重循环枚举状态i,j求出值v

设ans[i][v]表示i和j比较的值是v,所给串中j状态的个数

即ans[i][v] += cnt[j]

最后询问的时候,注意值小于等于k的都可以,因此每次询问求一个前缀和即可

code:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int w[15],cnt[5000],ans[5000][121];
char s[15];
int n,m,q;
int getval(int a,int b){
    int ret = 0;
    for(int i = n-1; i >= 0; i--){
        if((a & 1) == (b & 1)) ret += w[i];
        a >>= 1;
        b >>= 1;
    }
    return ret;
}
int change(){
    int num = 0;
    for(int j = 0; j < n; j++){
        num = num * 2 + (s[j] - '0');
    }
    return num;
}
int main(){
    memset(ans,0,sizeof(ans));
    memset(cnt,0,sizeof(cnt));
    scanf("%d%d%d",&n,&m,&q);
    for(int i = 0; i < n; i++){
        scanf("%d",&w[i]);
    }
    for(int i = 0; i < m; i++){
        scanf("%s",s);
        int num = change();
        cnt[num]++;
    }
    //预处理
    int S = 1 << n;
    for(int i = 0; i < S; i++){
        for(int j = 0; j < S ;j++){
            int v = getval(i,j);
            if(v <= 100) ans[i][v] += cnt[j];//题目要求k<=100所以只看100以内的就行
        }
    }
    while(q--){
        int k;
        scanf("%s%d",s,&k);
        int tmp = change();
        int ret = 0;
        for(int i = 0; i <= k; i++){
            ret += ans[tmp][i];
        }
        printf("%d\n",ret);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/codeswarrior/article/details/82389834
wu