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...)$ 的方案数。

 

题解

一开始口糊了一下,然后写代码的时候发现不少东西没考虑周到,于是就看起了题解。

我们首先需要发现一个重要的性质:

如果某一位上不受m限制(也就是选0或选1都可以)那么无论其它数的这一位位选什么都可以通过这一位来变成结果和K的这一位相等

为了避免来自$x <= m$的麻烦,我们首先让$m ++$, 使条件变为$x < m$。

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

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

然后第$j$位由于要小于m,所以m的这一位必然1,然后j的这一位必然是0

然后按照套路我们发现如果这一位我们选了0,那么后面的位随便选都不会大于m

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

我们记第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 }

 

转载于:https://www.cnblogs.com/CreeperLKF/p/9014438.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值