洛谷P7606 [THUPC2021] 混乱邪恶 题解
题意:
给你一个蜂窝网络,你初始在坐标 \((0,0)\) 。这个网络中每个交叉点共有 \(6\) 个相邻的交叉点
共有两种属性 \(L,R\) (守序值和善良值),初始为 \(0\) ,均在\(\bmod p\) 意义下存在。
现在有 \(n\) 张卡片,第 \(i\) 张使用后可以选择向任意方向(\(6\) 个方向)前进一步。
如果向 \(j\) 方向前进,则会使得 \(L\gets L+l_{i,j},\,R\gets R + r_{i,j}\) (在\(\bmod p\) 意义下)。
问是否有一种方案使得自己最后能够回到 \((0,0)\) 且 \(L=L_0,R=R_0\) 。
输入格式:
第一行两个正整数 \(n,p\)。
接下来 \(n\) 行,每行 \(2\times 6\) 个非负整数 \(l_{i,j},r_{i,j}~(1 \le j \le 6)\)。
最后一行两个非负整数 \(L_0,R_0\)。
输出格式:
如果能,输出一行
Chaotic Evil
。如果不能,输出一行
Not a true problem setter
。数据范围:
保证 \(1 \le n \le 100\),\(1 \le p \le 100\)。保证其他输入数据在 \(0\) 到 \(p-1\) 之间。
首先把这个蜂窝图摆正,那么就变成了一个多加了斜边的网格图。
设 \(f(i,l,r,x,y)\) 表示考虑前 \(i\) 张牌当前在 \(x,y\) 时能否使得 \(L,R\) 为 \(l,r\) 。
这里需要引入一个模型:随机游走。
一只蜘蛛原本在蛛网中央,现在它开始在网上乱走,步长为 \(1\) 个单位长度
但是最后又回到了原点,假设它一共走了 \(k\) 步,求它期望走到的相对于原点的最远距离为多少?
这个模型给出的答案是 \(\sqrt{n}\) 。
更复杂一些的模型是(其实这道题不需要这个模型,仅作介绍)
给你一个长度为 \(n\) 的数列 \(a\),要求从其中随机选取元素。
每个元素只能选一次,一直选完整个数列并且最后的和为 \(0\) 。
如果对于任意 \(1 \le i \le n\) 都有 \(|a_i| \le c\) ,求在整个选取过程中,
已选元素之和 \(S\) 的期望最大值和最小值分别为多少?
答案是 \(-c\sqrt{n} \le \mathrm{E}(S) \le c\sqrt{n}\) 。
不过和这道题的区别在于,我们可以选择怎么走,而不是乱走。
由于 \(x,y \le 100\) 而且我们无从得知可行的走法(除了强行 \(\mathcal{O}(n^3p^2)\) 的dp)。
所以我们可以开摆,把所有的卡牌随机一下,这样就相当于每次在随机瞎走了
由于期望的最远距离是 \(\sqrt{n}\) ,所以我们 \(x,y\) 只需要开到 \(\mathcal{O}(\sqrt{n})\) 即可,实际上开到 \(2\sqrt{n}\) 能保证正确率。
那么剩下的就是简单的 dp 了,然后 \(y\) 这一维可以用 bitset 来做(用 bitset 比 long long 快)。
时间复杂度 \(\mathcal{O}\left(\frac{n^2p^2}{\omega}\right)\)
代码:(常数巨大)
#include <bits/stdc++.h>
using namespace std;
// #define int long long
// #define INF 0x3f3f3f3f3f3f3f3f
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)(105))
#define M 51
mt19937 rd(time(0));
int n, p, a[N][M];
bitset<M> f[2][N][N][M];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n >> p; const int limit = 2 * (sqrt(n) + 1);
rep(i, 1, n) rep(j, 1, 12) cin >> a[i][j];
shuffle(a + 1, a + 1 + n, rd);
static const auto mod = [](int x) { while(x >= p) x -= p; return x; };
int L, R; cin >> L >> R; f[0][0][0][limit][limit] = true;
rep(i, 1, n) rep(l, 0, p - 1) rep(r, 0, p - 1) rep(x, 0, limit * 2)
{
const int cur = i & 1, pre = cur ^ 1;
auto &now = f[cur][l][r][x]; now.reset();
now |= f[pre][mod(l - a[i][1] + p)][mod(r - a[i][2] + p)][x] << 1;
now |= f[pre][mod(l - a[i][7] + p)][mod(r - a[i][8] + p)][x] >> 1;
if(x != xEND)
{
now |= f[pre][mod(l - a[i][3] + p)][mod(r - a[i][4] + p)][x + 1];
now |= f[pre][mod(l - a[i][5] + p)][mod(r - a[i][6] + p)][x + 1] >> 1;
}
if(x != 0)
{
now |= f[pre][mod(l - a[i][9] + p)][mod(r - a[i][10] + p)][x - 1];
now |= f[pre][mod(l - a[i][11] + p)][mod(r - a[i][12] + p)][x - 1] << 1;
}
}
if(f[n & 1][L][R][limit][limit]) cout << "Chaotic Evil\n";
else cout << "Not a true problem setter\n";
return 0;
}
参考文献:
[1] https://www.luogu.com.cn/article/1io8pjou
题外话:
守序中立。