A 1001 Link with Bracket Sequence II
题意:给定一个长度为n的数组,若a[i] == 0,那么表示这个位置填入的括号未知,若|a[i]|> 0 表示这个位置填入第|a[i]|种括号,左括号为正,右括号为负,求填满这个括号序列的方案数。
分析:n <= 500,因此我们考虑区间dp。对于所有括号序列,我们从两种情况进行转移,f[l][r]表示对于一个从范围为[l, r]的合法括号序列,且l和r处的括号序列位置相匹配的方案数。g[l][r]表示[l, r]的合法括号序列的方案数,且l和r处的括号序列位置相匹配,但是当 g 在转移过程中,g 表示取区间的左右端点的括号类型与 f 表示的区间的左右端点的括号类型不一定相同。
为什么要这么设定呢?因为括号的种类有m种,我们如何用这个m呢?就是枚举到两端均是0的时候直接可以乘一个m,但是这只是对于f来说的转移方程。而g是更加普遍的情况,如何转移呢?我们考虑区间分点,g[l][r]种找到一个分界线k,那么可以得出转移方程:g[l][r] += g[l][k - 1] * f[k][r]。这个堆叠方向是人为规定的,是为了避免重复计数。
其实只要想清楚如何防止重复统计就可以,我们一般都认为定义一种转移方式,转移方式有许多,比如这道题我们的 g 是从 g + f 的方式转移,这样恰好能够不重不漏计算出所有满足要求的 g 。
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <vector>
#include <set>
using namespace std;
typedef long long LL;
const int N = 510, M = N * 44 + 1, mod = 1e9 + 7;
LL a[N];
LL n, m, T;
LL f[N][N], g[N][N];
void solve()
{
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%lld", &a[i]);
memset(f, 0, sizeof f);
memset(g, 0, sizeof g);
if (n & 1)
{
puts("0");
return;
}
for (int i = 0; i <= n; i ++ ) g[i + 1][i] = 1;
for (int len = 2; len <= n; len += 2)
for (int l = 1; l + len - 1 <= n; l ++ )
{
int r = l + len - 1;
if (a[l] >= 0 && a[r] <= 0)
{
int t = 0;
if (a[l] == 0 && a[r] == 0) t = m;
else if (a[l] == 0 || a[r] == 0) t = 1;
else if (a[l] + a[r] == 0) t = 1;
else t = 0;
f[l][r] = g[l + 1][r - 1] * t % mod;
}
for (int k = l; k <= r; k += 2)
g[l][r] = (g[l][r] + g[l][k - 1] * f[k][r] % mod) % mod;
}
printf("%lld\n", g[1][n]);
}
signed main()
{
scanf("%lld", &T);
while (T -- )
{
solve();
}
return 0;
}
K 1011 Link is as bear
题意:给定一个长度为n的数组,指定一种操作,可以使得[l, r]区间内所有值变成a[l] ^ a[l + 1] .. ^ a[r]的值,求最终使得整个数组变为同一个数的最大可能值。
分析:题目有个特殊点在于一定存在两个同等的数相邻。那么这有什么用呢?其实比较复杂我们不如直接看题解的证明,证明完就相当于我们可以构造出任意一个解,你可以随便挑几个数来构成解。那么这该怎么做呢?线性基。线性基就是建立在高斯消元的基础上的求基底的过程。
1.对两个数操作两次可以变成0 0
2.只要存在连续两个的0,就可以实现取任何数,例:对于 0 0 a b, 若想取b,则可以先让a和0异或两次变成 0 0 0 b -> b b b b。
结论:本题考察给定 n 个整数(可能重复),从中挑选任意个整数,使得选出整数的异或和最大。
代码如下:
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#include <vector>
#include <set>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10, M = N * 44 + 1, mod = 1e9 + 7;
LL a[N];
int n, T;
void solve()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%lld", &a[i]);
int r = 1;
for (int c = 62; c >= 0; c -- )
{
for (int i = r; i <= n; i ++ )
{
if (a[i] >> c & 1)
{
swap(a[i], a[r]);
break;
}
}
if (!(a[r] >> c & 1)) continue;//此时整列是0
for (int i = 1; i <= n; i ++ )
if (i != r && (a[i] >> c & 1))
a[i] ^= a[r];
r ++ ;
if (r == n + 1) break;//全部处理完了
}
LL res = 0;
for (int i = 1; i <= r; i ++ ) res ^= a[i];
printf("%lld\n", res);
}
int main()
{
scanf("%d", &T);
while (T -- )
{
solve();
}
return 0;
}