HDU3693 Math Teacher's Homework ---- 数位DP

HDU3693 Math Teacher's Homework

一句话题意

给定$n, k以及m_1, m_2, m_3, ..., m_n$求$x_1 \oplus x_2 \oplus x_3 \oplus ... \oplus x_n == K(x_1 \leq m_1, x_2 \leq m_2...)$ 的方案数。spa

 

题解

一开始口糊了一下,而后写代码的时候发现很多东西没考虑周到,因而就看起了题解。code

咱们首先须要发现一个重要的性质:blog

若是某一位上不受m限制(也就是选0或选1均可以)那么不管其它数的这一位位选什么均可以经过这一位来变成结果和K的这一位相等string

为了不来自$x <= m$的麻烦,咱们首先让$m ++$, 使条件变为$x < m$。it

而后按照数位dp的套路对位分析。io

首先假设咱们处理到了第j位,而后从高位到$j + 1$位都已经到了最大值class

而后第$j$位因为要小于m,因此m的这一位必然1,而后j的这一位必然是0统计

而后按照套路咱们发现若是这一位咱们选了0,那么后面的位随便选都不会大于mstatic

为了方便dp,咱们令每一位最先容许随便选的那个$x_i$为$A_j$,这个数将在后面被限制以使其它自由位达到K上对应位的要求di

咱们记第j位上的第i个数字为自由的,当且仅当这个位不是被m限制了(即第i个数从高位枚举到第一个比m小的位置),且不是那些被最先选择(上一行的定义)限制的数字。

而后每一位上的方案数就是$2^{自由数个数-1}$

因而咱们设$dp[i][j][0/1]$表示第i个数的“第i个数从高位枚举到第一个比m小的位置”为j,此位的异或值为0/1

下面咱们记

$num[i][j]$为第i个数字第j位

$sum[i][j]$为j位上从第一个数字异或到第i个数字的结果

而后分状况(自由数位置)讨论从状态$dp[i - 1][k][r]$(注意大小写)(注意下面$2^x$的下标)转移,若

m此位能够有不一样限制,即$num[i][j] == 1$

$j < k$:$dp[i][j][sum[i - 1][j]] += dp[i - 1][k][r] * 2^k$

$j > k$:$dp[i][k][r \oplus sum[i - 1][j]] += dp[i - 1][k][r] * 2^j$

$j == k$:$dp[i][j][r] += dp[i - 1][k][r] * 2^k$

最后要求$k[j] == sum[n][j]$的时候才能统计入答案

(然而我并不知道怎么用Latex打出'^' ......)

代码以下:

 

 1 #include <cstdio>
 2 
 3 #include <bitset>
 4 #include <cstring>
 5 
 6 using namespace std;
 7 
 8 char buf[11111111], *pc = buf;
 9 
10 inline void Main_Init(){
11     static bool inited = false;
12     if(inited) fclose(stdin), fclose(stdout);
13     else {
14         fread(buf, 1, 11111111, stdin);
15         inited = true;
16     }
17 }
18 
19 inline int read(){
20     int num = 0;
21     char c;
22     while((c = *pc ++) < 48);
23     while(num = num * 10 + c - 48, (c = *pc ++) >= 48);
24     return num;
25 }
26 
27 //Source Code
28 
29 const int MAXN = 55;
30 const int MAXM = 33;
31 const int MODS = 1000000003;
32 
33 int n, ans;
34 int x[MAXN];
35 unsigned int bin[MAXM];
36 int dp[MAXN][MAXM][2];
37 
38 bitset<32> K, num[MAXN], sum[MAXN];
39 
40 int main(){
41     Main_Init();
42     for(int i = 0; i < 32; i++) bin[i] = 1 << i;
43     while(n = read(), n){
44         K = read();
45         for(int i = 1; i <= n; i++)
46             num[i] = x[i] = read() + 1;
47         memset(sum, 0, sizeof(sum)), memset(dp, 0, sizeof(dp));
48         ans = 0;
49         dp[0][0][0] = 1;
50         for(int j = 0; j < 32; j++) sum[0][j] = num[0][j];
51         for(int i = 1; i <= n; i++)
52             for(int j = 0; j < 32; j++)
53                 sum[i][j] = sum[i - 1][j] ^ num[i][j];
54         for(int i = 1; i <= n; i++){
55             for(int j = 0; j < 32; j++){
56                 if(!num[i][j]) continue;
57                 for(int k = 0; k < 32; k++){
58                     for(int r = 0; r < 2; r++){
59                         if(dp[i - 1][k][r]){
60                             if(j > k) dp[i][j][sum[i - 1][j]] = (dp[i][j][sum[i - 1][j]] + 1ll * dp[i - 1][k][r] * bin[k]) % MODS;
61                             else if(j < k) dp[i][k][r ^ num[i][k]] = (dp[i][k][r ^ num[i][k]] + 1ll * dp[i - 1][k][r] * bin[j]) % MODS;
62                             else dp[i][j][r] = (dp[i][j][r] + 1ll * dp[i - 1][k][r] * bin[k]) % MODS;
63                         }
64                     }
65                 }
66             }
67         }
68         for(int i = 31; i >= 0 && K[i + 1] == sum[n][i + 1]; i--)
69             ans = (1ll * ans + dp[n][i][K[i]]) % MODS;
70         printf("%d\n", ans);
71     }
72     Main_Init();
73     return 0;
74 }
相关文章
相关标签/搜索