设 f[i][j]
f
[
i
]
[
j
]
表示处理到第 i
i
个围栏,第 i ~ i+4
i
+
4
个围栏的状态为 j
j
最多能使多少个小朋友高兴,num[i][j] 表示对应条件下仅确定这五个围栏的状态就能使多少小朋友高兴。
则转移为 f[i][j]=max{f[i−1][(j
f
[
i
]
[
j
]
=
m
a
x
{
f
[
i
−
1
]
[
(
j
& 15)<<1],f[i−1][(j
15
)
<<
1
]
,
f
[
i
−
1
]
[
(
j
& 15)<<1|1]}+num[i][j]
15
)
<<
1
|
1
]
}
+
n
u
m
[
i
]
[
j
]
,即表示由第 i−1
i
−
1
个围栏移走或不移走转移过来。
其中 num[i][j]
n
u
m
[
i
]
[
j
]
可以预处理,预处理复杂度 O(25×c)
O
(
2
5
×
c
)
。
还有一个问题:如何处理环呢?也就是我们应如何处理前 4 个围栏的状态对最后几个围栏转移时的影响。
我们不妨枚举前 5 个围栏的状态,每次都做一遍 DP
D
P
,第 n−3
n
−
3
~ n
n
个围栏即根据确定的状态转移,DP 复杂度 O(210×n)
O
(
2
10
×
n
)
,所以总复杂度 O(25×c+210×n)
O
(
2
5
×
c
+
2
10
×
n
)
。
Code
#include <cstdio>#include <cctype>#include <iostream>#include <cstring>#include <algorithm>usingnamespacestd;
inlineint get()
{
char ch; int res = 0; bool flag = false;
while (ch = getchar(), !isdigit(ch) && ch != '-');
(ch == '-' ? flag = true : res = ch ^ 48);
while (ch = getchar(), isdigit(ch))
res = res * 10 + ch - 48;
return flag ? -res : res;
}
constint Minn = -0x3f3f3f3f;
constint C = 32;
constint N = 1e4 + 5, M = 5e4 + 5;
int n, c, Ans;
int a[N], uk[M], vk[M];
int f[N][C], num[N][C];
inlineint Max(int x, int y) {return x > y ? x : y;}
inlinevoid CkMax(int &x, int y) {if (x < y) x = y;}
inlineint nxt(int x) {return x == n ? 1 : x + 1;}
int main()
{
n = get(); c = get(); int E, F, L, x;
while (c--)
{
x = E = get(); F = get(); L = get();
for (int i = 1; i <= 5; ++i)
a[x] = i, x = nxt(x);
for (int i = 1; i <= F; ++i) uk[i] = get();
for (int i = 1; i <= L; ++i) vk[i] = get();
for (int i = 0; i < C; ++i)
{
bool flag = false;
for (int j = 1; j <= F; ++j)
if (a[uk[j]] && !(1 << a[uk[j]] - 1 & i))
{
flag = true;
break;
}
for (int j = 1; j <= L; ++j)
if (a[vk[j]] && (1 << a[vk[j]] - 1 & i))
{
flag = true;
break;
}
if (flag) ++num[E][i];
}
x = E;
for (int i = 1; i <= 5; ++i)
a[x] = 0, x = nxt(x);
}
for (int k = 0; k < C; ++k)
{
x = k;
for (int i = 1; i <= 5; ++i)
a[i] = x & 1, x >>= 1;
for (int i = 1; i <= n; ++i)
for (int j = 0; j < C; ++j)
f[i][j] = Minn;
f[1][k] = num[1][k];
for (int i = 2, im = n - 4; i <= im; ++i)
for (int j = 0; j < C; ++j)
CkMax(f[i][j], Max(f[i - 1][(j & 15) << 1],
f[i - 1][(j & 15) << 1 | 1]) + num[i][j]);
for (int i = n - 3; i <= n; ++i)
{
int tmp = n - i + 1;
for (int j = 0, jm = 1 << tmp; j < jm; ++j)
{
x = j;
for (int k = tmp + 1; k <= 5; ++k)
if (a[k - tmp]) x |= 1 << k - 1;
CkMax(f[i][x], Max(f[i - 1][(x & 15) << 1],
f[i - 1][(x & 15) << 1 | 1]) + num[i][x]);
}
}
for (int j = 0; j < C; ++j)
CkMax(Ans, f[n][j]);
}
printf("%d\n", Ans);
}