Codeforces 979E Kuro and Topological Parity - 动态规划 - 组合数学

题目传送门

  传送点

题目大意

  给定$n$个标号依次为$1, 2, \cdots, n$的点,其中一些点被染成一些颜色,剩下的点没有染色。你需要添加一些有向边并将剩下的点染色,满足有向边从编号小的一端指向编号大的一端,图中所有黑白相错的路径的条数与$p$对2取模同余。

  $1\leqslant n\leqslant 10^6$

  想一下如何求DAG中黑白相错的路径的条数。用$g_{i}$表示$i$结尾的路径的条数。

  考虑怎么转移,枚举前一个点,然后$g_{i} += g_{pre}[col_{pre}\neq col_{i}]$。

  这里我们希望知道所有点的$g$的和的奇偶性。我们考虑每次加入一个点,我们希望知道它的$g$的奇偶性就能更新了。

  它更新的时候的奇偶性只与$g_{pre}$的奇偶性以及$g$的颜色有关,因此我们可以将点分为四类:

  • 奇黑点,$g_{p} \equiv 1 \pmod{2} \wedge col_{i} = black$
  • 偶黑点,$g_{p} \equiv 0 \pmod{2} \wedge col_{i} = black$
  • 奇白点,$g_{p} \equiv 1 \pmod{2} \wedge col_{i} = white$
  • 偶白点,$g_{p} \equiv 0 \pmod{2} \wedge col_{i} = white$

  然后假设当前的即将加入的点是白点,那么考虑它的连边

  • 之前的白点对它的奇偶性没有影响,所以这之间的边可以任意连。
  • 偶黑点对它的奇偶性也没有影响,所以这之间的边可以任意连。
  • 考虑奇黑点
    • 如果不存在奇黑点,那么当前点一定是奇白点(它自己的方案)。
    • 如果存在奇黑点,每与一个奇黑点连边就会改变一次奇偶性,那么我们先拿走一个,剩下的任意连,拿走的这一个可以控制这个点方案数的奇偶性(比如考虑这个点之前当前点是奇白点,我希望它是奇白点,那么不连边)。因此恰好一半的任意连边方法使当前点的奇偶性为奇或偶。

  对黑点可以作类似的讨论,然后我们可以愉快地得出结论:

  • 如果当前不存在方案数为奇数的颜色与当前点相反的点,那么之前方案数乘上$2^{i - 1}$转移到当前点方案数为奇数的状态。
  • 否则,对于当前点方案数为奇为偶各加上之前方案数乘$2^{i - 2}$。

  状态用$f_{i, have\_odd\_white, have\_odd\_black, parity}$来表示。

  时间复杂度$O(n)$。

Code

 1 /**
 2  * Codeforces
 3  * Problem#979E
 4  * Accepted
 5  * Time: 31ms
 6  * Memory: 36100k
 7  */ 
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 typedef bool boolean;
11 
12 const int N = 55, M = 1e9 + 7;
13 
14 int add(int a, int b) {
15     a += b;
16     if (a >= M)
17         return a - M;
18     return a;
19 }
20 
21 int n, p;
22 int ar[N];
23 int pow2[N];
24 int f[N][N][N][N];
25 int C[N][N];
26 int Co[N], Ce[N];
27 
28 inline void init() {
29     scanf("%d%d", &n, &p);
30     for (int i = 1; i <= n; i++)
31         scanf("%d", ar + i);
32 }
33 
34 inline void prepare() {
35     pow2[0] = 1;
36     for (int i = 1; i <= n; i++)
37         pow2[i] = add(pow2[i - 1], pow2[i - 1]);
38 
39     C[0][0] = 1;
40     for (int i = 1; i <= n; i++) {
41         C[i][0] = C[i][i] = 1;
42         for (int j = 1; j < n; j++)
43             C[i][j] = add(C[i - 1][j], C[i - 1][j - 1]);
44     }
45 
46     for (int i = 0; i <= n; i++) {
47         for (int j = 0; j <= n; j++)
48             if (j & 1) {
49                 Co[i] = add(Co[i], C[i][j]);
50             } else {
51                 Ce[i] = add(Ce[i], C[i][j]);
52             }
53     }
54 }
55 
56 inline void solve() {
57     f[0][0][0][0] = 1;
58     for (int i = 1; i <= n; i++)
59         // enmuerating last status
60         for (int ob = 0; ob < i; ob++)    // odd black
61             for (int eb = 0; ob + eb < i; eb++)    // even black
62                 for (int ow = 0; ob + eb + ow < i; ow++) {    // odd white
63                     int lans = f[i - 1][ob][eb][ow];
64                     if (!lans)
65                         continue;
66                     int ew = i - ob - eb - ow - 1;
67                     if (ar[i] != 0) {    // here painted in white
68                         int gama = pow2[ow + ew + eb];
69                         f[i][ob][eb][ow] = add(f[i][ob][eb][ow], lans * 1ll * gama % M * Co[ob] % M);
70                         f[i][ob][eb][ow + 1] = add(f[i][ob][eb][ow + 1], lans * 1ll * gama % M * Ce[ob] % M);
71                     }
72 
73                     if (ar[i] != 1) {    // here painted in black
74                         int gama = pow2[ew + ob + eb];
75                         f[i][ob][eb + 1][ow] = add(f[i][ob][eb + 1][ow], lans * 1ll * gama % M * Co[ow] % M);
76                         f[i][ob + 1][eb][ow] = add(f[i][ob + 1][eb][ow], lans * 1ll * gama % M * Ce[ow] % M);
77                     }
78                 }
79 
80     int res = 0;
81     for (int ob = 0; ob <= n; ob++)
82         for (int eb = 0; eb + ob <= n; eb++)
83             for (int ow = 0; ow + eb + ob <= n; ow++)
84                 if (((ob + ow) & 1) == p)
85                     res = add(res, f[n][ob][eb][ow]);
86     printf("%d\n", res);
87 }
88 
89 int main() {
90     init();
91     prepare();
92     solve();
93     return 0;
94 }
Slower Solution
 1 /**
 2  * Codeforces
 3  * Problem#979E
 4  * Accepted
 5  * Time: 31ms
 6  * Memory: 300k
 7  */
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 typedef bool boolean;
11 
12 const int N = 55, M = 1e9 + 7;
13 
14 int add(int a, int b) {
15     a += b;
16     if (a >= M)
17         return a - M;
18     return a;
19 }
20 
21 int n, p;
22 int ar[N];
23 int pow2[N];
24 int f[N][2][2][2];
25 
26 inline void init() {
27     scanf("%d%d", &n, &p);
28     for (int i = 1; i <= n; i++)
29         scanf("%d", ar + i);
30     pow2[0] = 1;
31     for (int i = 1; i <= n; i++)
32         pow2[i] = add(pow2[i - 1], pow2[i - 1]);
33 }
34 
35 inline void solve() {
36     f[0][0][0][0] = 1;
37     for (int i = 1; i <= n; i++)
38         // enmuerating last status
39         for (int hob = 0; hob < 2; hob++)    // exists odd black or not
40             for (int how = 0; how < 2; how++)    // exists odd white or not
41                 for (int par = 0; par < 2; par++) {
42                     int lans = f[i - 1][hob][how][par];
43                     if (!lans)
44                         continue;
45                     if (ar[i] != 0) {        // here painted in white
46                         if (!hob)
47                             f[i][0][1][par ^ 1] = add(f[i][0][1][par ^ 1], lans * 1ll * pow2[i - 1] % M);
48                         else {
49                             f[i][1][1][par ^ 1] = add(f[i][1][1][par ^ 1], lans * 1ll * pow2[i - 2] % M);
50                             f[i][1][how][par] = add(f[i][1][how][par], lans * 1ll * pow2[i - 2] % M);
51                         }
52                     }
53 
54                     if (ar[i] != 1)    {    // here painted in black
55                         if (!how)
56                             f[i][1][0][par ^ 1] = add(f[i][1][0][par ^ 1], lans * 1ll * pow2[i - 1] % M);
57                         else {
58                             f[i][1][1][par ^ 1] = add(f[i][1][1][par ^ 1], lans * 1ll * pow2[i - 2] % M);
59                             f[i][hob][1][par] = add(f[i][hob][1][par], lans * 1ll * pow2[i - 2] % M);
60                         }
61                     }
62                 }
63     int res = 0;
64     for (int x = 0; x < 2; x++)            
65         for (int y = 0; y < 2; y++)
66             res = add(res, f[n][x][y][p]);
67     printf("%d\n", res);
68 }
69 
70 int main() {
71     init();
72     solve();
73     return 0;
74 }
posted @ 2018-09-22 20:36 阿波罗2003 阅读(...) 评论(...) 编辑 收藏