2023/3/16数论总结(未完)
CF1305F
随机化经典例题
考虑某题正解是随机化的话, 要满足什么? 正确率很高, 或者每次的正确率是一个比较大的数(比如 \(0.5\) )。
回到这道题, 我们首先考虑如果将所有数变成 \(2\) 的最小步数, 其一定小于等于 \(n\) , 这一点应该很明显, 最劣情况是所有数都是奇数, 那也只需要 \(n\) 步就可以使所有数满足条件。 既然如此, 我们考虑最后所有数变成了其它的数的因数, 会是什么情况。 此时, 假设这个因数是 \(x\) , 那么至少一半的数满足以下三种情况中的一个:其本身是 \(x\) 的倍数, 其加上一后是 \(x\) 的倍数, 其减去一后是 \(x\) 的倍数。如果不这样, 那么必然有大于 \(n/2\) 的数需要操作大于等于 \(2\) 次, 那么操作数就比操作成 \(2\) 的倍数的操作数要多了, 综上, 我们随机选择序中的一个数 \(a_i\) 那么我们有大于等于 \(1/2\) 的几率答案需要的因数是 \(a_i\) 或 \(a_i + 1\) 或 \(a_i - 1\) 的因数。 选多次后就有很大的几率选中正确答案。
SPOJ11414
这道题算是博弈论的板子题目
首先我们需要搞清楚 SG 函数, 这个函数其实就是把所有的操作双方等价操作的博弈问题转到 DAG 中后再转化成 NIM 游戏。 既然如此, 我们发现这就是典型的那种问题, 考虑构造 SG 函数。对于一个点,我们将其整个子树的 SG 函数设为 \(f(i)\) , 我们再设 \(i\) 子树中操作了一个点后的 SG 函数为 \(g(i, j)\) 。 发现 \(g(i, j)\) 其实就是几个互不相干的子树所组成的森林, 根据 SG 函数的性质, 直接转化成异或就行了, 考虑到子树需要挨个处理, 而对于 \(g(i, j)\) , 我们其实是可以直接转化成 \(g(fa(i), j)\) 的( \(fa(i)\) 表示 \(i\) 的父亲), 只需要再异或上 \(fa(i)\) 的其它儿子的 \(f(u)\) 的异或和就行了, 所以我们可以 \(trie\) 树合并, 具体过程就不再赘述。
代码:
#include <cstdio>
#include <algorithm>
#include <stack>
using namespace std;
#define MAXN 100000
#define MAXM 17
bool sc[MAXN + 5];
bool vis[MAXN + 5];
struct node {
int ed;
node *next;
}*s[MAXN + 5];
struct trie {
int num = 0;
int k = 0;
int Lazy = 0;
trie *next[2] = {};
}*rt[MAXN + 5];
int sg[MAXN + 5];
int Fa[MAXN + 5];
int sk[MAXN + 5];
int tot = 0;
#undef MAXN
void push (int u, int v) {
node *p = new node;
p->ed = v;
p->next = s[u];
s[u] = p;
}
int find (int x) {
if (Fa[x] != Fa[Fa[x]]) {
Fa[x] = find (Fa[x]);
}
return Fa[x];
}
void op (int x, int y) {
Fa[find (x)] = find (y);
}
void init (int now, int fa) {
vis[now] = sc[now];
for (node *i = s[now]; i; i = i->next) {
if (i->ed == fa) {
continue;
}
init (i->ed, now);
vis[now] &= vis[i->ed];
}
}
void download (trie *p, int loc) {
if (!p) {
return ;
}
if ((!p->next[0] && !p->next[1]) || !p->Lazy) {
p->Lazy = 0;
return ;
}
if (p->Lazy & (1 << loc)) {
swap (p->next[0], p->next[1]);
}
if (p->next[0]) {
p->next[0]->Lazy ^= p->Lazy;
}
if (p->next[1]) {
p->next[1]->Lazy ^= p->Lazy;
}
p->Lazy = 0;
}
void change (trie *r, int now, int v) {
stack <trie*> S;
for (int i = MAXM; i >= 0; i--) {
S.push(r);
bool dir = ((1 << i) & v);
if (!r->next[dir]) {
r->next[dir] = new trie;
}
r = r->next[dir];
}
r->num = 1;
if (!r->k) {
r->k = ++tot;
Fa[tot] = tot;
}
sk[now] = r->k;
while (!S.empty ()) {
S.top()->num = 0;
if (S.top()->next[0]) {
S.top()->num += S.top()->next[0]->num;
}
if (S.top()->next[1]) {
S.top()->num += S.top()->next[1]->num;
}
S.pop();
}
}
void merge (trie *&a, trie *b, int d) {
if (a == 0) {
a = b;
return ;
}
else if (b == 0) {
return ;
}
if (d < 0) {
op (a->k, b->k);
a->num = (a->num | b->num);
return ;
}
download (a, d);
download (b, d);
merge (a->next[0], b->next[0], d - 1);
merge (a->next[1], b->next[1], d - 1);
a->num = 0;
if (a->next[0]) {
a->num += a->next[0]->num;
}
if (a->next[1]) {
a->num += a->next[1]->num;
}
}
int find (trie* a, int d) {
if (!a || d < 0) {
return 0;
}
download (a, d);
if (a->next[0] && a->next[0]->num == (1 << d)) {
return find (a->next[1], d - 1) | (1 << d);
}
else {
return find (a->next[0], d - 1);
}
}
void dfs (int now, int fa) {
int sum = 0;
for (node *i = s[now]; i; i = i->next) {
if (i->ed == fa || vis[i->ed]) {
continue;
}
dfs (i->ed, now);
sum ^= sg[i->ed];
}
for (node *i = s[now]; i; i = i->next) {
if (i->ed == fa || vis[i->ed]) {
continue;
}
rt[i->ed]->Lazy ^= (sum ^ sg[i->ed]);
merge (rt[now], rt[i->ed], MAXM);
}
if (!rt[now]) {
rt[now] = new trie;
}
if (!sc[now]) {
change (rt[now], now, sum);
}
sg[now] = find (rt[now], MAXM);
}
int main () {
int n;
scanf ("%d", &n);
for (int i = 1; i <= n; i ++) {
int v;
scanf ("%d", &v);
sc[i] = v;
}
for (int i = 1; i < n; i ++) {
int u, v;
scanf ("%d %d", &u, &v);
push (u, v);
push (v, u);
}
init (1, 0);
if (vis[1]) {
printf ("-1");
return 0;
}
dfs (1, 0);
if (!sg[1]) {
printf ("-1");
}
else {
trie *p = rt[1];
for (int i = MAXM; i >= 0; i --) {
download(p, i);
p = p->next[0];
}
for (int i = 1; i <= n; i ++) {
if (find (sk[i]) == find(p->k)) {
printf ("%d ", i);
}
}
}
}
CF1034E
首先, 这是一道子集卷积的板子, 然后我们贺上去后会发现过不了, 考虑如何优化。
首先我们不考虑子集卷积, 就直接考虑 FWT 。 按照子集卷积的思路来, 把 \(j \& k = 0\) 这一限制改成 \(count(j) + count(k) = count(i)\) (其中 \(count(i)\) 表示 \(i\) 二进制下 \(1\) 的个数), 但是接着, 我们不考虑枚举, 而是选择再次转化, 变成 \(4^{count(j) + count(k)} = 4^{count(i)}\) 接着, 我们再考虑将每个 \(a_j\) 和 \(b_k\) 都改写成 \(a_j * 4^{count(j)}\) 和 \(b_k * 4^ {count(k)}\) 。 这时候, \(count (j) + count (k) < count(i)\) 肯定不可能发生(否则无法满足 \(j | k = i\) ) , 而 \(count (j) + count (k) > count(i)\) 时因为 \(4^{count(j) + count(k)} > 4^{count(i)}\) 所以除了以后会被 \(mod 4\) 模掉, 最后直接 FWT 即可。

浙公网安备 33010602011771号