2023/3/16数论总结(未完)

1|0CF1305F

随机化经典例题
考虑某题正解是随机化的话, 要满足什么? 正确率很高, 或者每次的正确率是一个比较大的数(比如 0.5 )。
回到这道题, 我们首先考虑如果将所有数变成 2 的最小步数, 其一定小于等于 n , 这一点应该很明显, 最劣情况是所有数都是奇数, 那也只需要 n 步就可以使所有数满足条件。 既然如此, 我们考虑最后所有数变成了其它的数的因数, 会是什么情况。 此时, 假设这个因数是 x , 那么至少一半的数满足以下三种情况中的一个:其本身是 x 的倍数, 其加上一后是 x 的倍数, 其减去一后是 x 的倍数。如果不这样, 那么必然有大于 n/2 的数需要操作大于等于 2 次, 那么操作数就比操作成 2 的倍数的操作数要多了, 综上, 我们随机选择序中的一个数 ai 那么我们有大于等于 1/2 的几率答案需要的因数是 aiai+1ai1 的因数。 选多次后就有很大的几率选中正确答案。

2|0SPOJ11414

这道题算是博弈论的板子题目
首先我们需要搞清楚 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); } } } }

3|0CF1034E

首先, 这是一道子集卷积的板子, 然后我们贺上去后会发现过不了, 考虑如何优化。
首先我们不考虑子集卷积, 就直接考虑 FWT 。 按照子集卷积的思路来, 把 j&k=0 这一限制改成 count(j)+count(k)=count(i) (其中 count(i) 表示 i 二进制下 1 的个数), 但是接着, 我们不考虑枚举, 而是选择再次转化, 变成 4count(j)+count(k)=4count(i) 接着, 我们再考虑将每个 ajbk 都改写成 aj4count(j)bk4count(k) 。 这时候, count(j)+count(k)<count(i) 肯定不可能发生(否则无法满足 j|k=i ) , 而 count(j)+count(k)>count(i) 时因为 4count(j)+count(k)>4count(i) 所以除了以后会被 mod4 模掉, 最后直接 FWT 即可。


__EOF__

本文作者FlowerDream
本文链接https://www.cnblogs.com/flower-dream/p/17224534.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   小篪篪  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Live2D
欢迎阅读『2023/3/16数论总结(未完)』
点击右上角即可分享
微信分享提示