bzoj 1879 [Sdoi2009]Bill的挑战

一般化的题目描述:

有$n$个属性,求恰好满足$k$个属性的物品个数

你有一个函数$q(S)$,可以快速算出至少满足属性集合$S$的物品个数(保证每个物品最多只会被统计一次)

设$f(S)$表示只满足属性集合$S$中的物品的个数,那么答案就是$\sum_{S \subseteq U \wedge |S| = k}f(S)$

设$f(S)=\sum\limits_{S \subseteq P}q(P)g(|P|)$,其中$|S|=k$,显然有$g(x)=(-1)^{x-k}$

证明:

对于一个物品$t$,假设它的属性集合为$Q$,且$S \subseteq Q$,且$|Q|=k+u$

那么它对$f(S)$的贡献是

$$\sum_{i=0}^{u}{u \choose i}(-1)^{i}=(-1+1)^{u}=[u=0]$$

也就是说$f(S)=\sum\limits_{S \subseteq P}q(P)(-1)^{|P|-k}$

也就是说

$$\begin{aligned}T&=\sum\limits_{S \subseteq U \wedge |S| = k}f(S) \\&=\sum\limits_{S \subseteq U \wedge |S| = k} \sum_{S \subseteq P}q(P)(-1)^{|P|-k} \\&=\sum\limits_{P \subseteq U \wedge |P| \ge k} q(P)(-1)^{|P|-k}{|P| \choose k}\end{aligned}$$

于是就做完了……

由于这道题的$q(S)$求法十分简单,在此就不赘述了

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int mod = 1e6 + 3;
 5 char s[20][55];
 6 int n, k, vis[55], len;
 7 ll pw(ll a, ll b) {
 8     ll r = 1;
 9     for( ; b ; b >>= 1, a = a * a % mod) if(b & 1) r = r * a % mod;
10     return r;
11 }
12 ll calc(int s) {
13     for(int i = 1 ; i <= len ; ++ i) {
14         vis[i] = 0;
15     }
16     for(int i = 1 ; i <= n ; ++ i) {
17         if((s >> (i - 1)) & 1) {
18             for(int j = 1 ; j <= len ; ++ j) {
19                 char c = :: s[i][j];
20                 if(c == '?') continue;
21                 if(vis[j] && vis[j] != c) {
22                     return 0;
23                 }
24                 vis[j] = c;
25             }
26         }
27     }
28     
29     ll res = 1;
30     for(int i = 1 ; i <= len ; ++ i) {
31         if(vis[i]) continue;
32         res = res * 26 % mod;
33     }
34     
35     return res;
36 }
37 
38 int C[20][20];
39 
40 void sol() {
41     scanf("%d%d", &n, &k);
42     for(int i = 1 ; i <= n ; ++ i) {
43         scanf("%s", s[i] + 1);
44     }
45     len = strlen(s[1] + 1);
46     int ans = 0;
47     for(int s = 0 ; s < (1 << n) ; ++ s) {
48         int cnt = 0;
49         for(int i = 1 ; i <= n ; ++ i) {
50             if((s >> (i - 1)) & 1) {
51                 ++ cnt;
52             }
53         }
54         if(cnt >= k) {
55             ll sig = ((cnt - k) & 1) ? -1 : 1;
56             ans = (ans + sig * C[cnt][k] * calc(s)) % mod;
57         }
58     }
59     ans = (ans % mod + mod) % mod;
60     printf("%d\n", ans);
61 }
62 
63 int main() {
64     C[0][0] = 1;
65     for(int i = 1 ; i < 20 ; ++ i) {
66         C[i][0] = 1;
67         for(int j = 1 ; j < 20 ; ++ j) {
68             C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
69         }
70     }
71     int T; scanf("%d", &T);
72     while(T --) sol(); 
73 }
bzoj 1879 [Sdoi2009]Bill的挑战

posted @ 2018-09-08 21:54  KingSann  阅读(161)  评论(0编辑  收藏  举报