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 }