闲话

明日CSP,而我连廊桥都不会分配
我是不是要抱泠了

于是今日晚上写一些板子

480p
soytony:斯大林p
这是什么新p主能不能各位给我科普一下

今日歌词是摘了美影日记里新的那部分!
今天推的歌是美影日记!希望各位能去看看新的pv

雨のち曇り晴れ

優しくきっと晴れ

きらきら浮かぶ言葉

もう忘れちゃうけれど

杂题

给定两个整数 \(n, m\) 和两个字符串 \(w_1, w_2\)\(n\) 是两个字符串的长度,\(m\) 是答案 \(P\) 的下界,\(n, m \le {10}^5\)

定义 Hash 函数如下

\[H_p(w)=\sum_{i=0}^{|w|-1}w_ir^i\bmod P \]

其中 \(P\) 是一个质数。

请你寻找任意一对 \(P,r\) 满足 \(H_p(w_1)=H_p(w_2)\),题目保证至少存在一组可行解。

\(m\leqslant P\leqslant 10^9\)\(2\leqslant r\leqslant P-2\)

仍然是翻译的官方题解。

问题可以被简单陈述如下:给定一个整系数多项式 \(f(x) = \sum_{i=0}^{n-1} f_ix^i\),找到一个大质数 \(p\) 以及 \(r \in [2, p-2]\) 使得 \(f(r) \equiv 0 \pmod p\)

一种解法是随机找到一个质数 \(p\),并依次检查所有 \(r\) 的取值。由于对于 \(r\) 的随机值来说 \(f(r) \text{ mod } p\) 也可以被认为是随机的,因此我们可以粗略地认为所有 \(r\) 都不满足要求的概率为

\[\left(1-\frac 1p\right)^p \approx e^{-1} \ge \frac 13 \]

因此我们只需重复 \(O(1)\) 次质数的选取。使用多项式多点求值能做到大常数的 \(O(n \log^2 n)\)。这足以解决问题,但这不是最优秀的做法,也不是最优雅的方法 : )

下面将讲述另一种做法。我们令 \(d\)\(p-1\) 的任意因子。众所周知,在模 \(p\) 整数域内有恰好 \(d\) 个元素 \(r\) 满足

\[r^d \equiv 1 \pmod p \]

注意到如果 \(r\) 是这样一个元素,则以 \(d=3\) 为例,有

\[r^{10} + r^5 \equiv r^{3\times 3 + 1} + r^{3+2} \equiv (r^3)^3 \times r + r^3\times r^2\equiv r +r^2 \pmod p \]

事实上,如果我们将这一性质应用到整个多项式上,我们就可以将同余项的系数合并,得到一个更小度数的多项式

\[f^{(d)}(r) = \sum_{i=0}^{d-1}\alpha_ir^i \]

这样求 \(d\) 个点只需要 \(O(d^2)\) 的复杂度。

当前算法的过程如下:寻找一个小整数 \(d\),选择一个形如 \(kd+1\) 的质数 \(p > m\),随后找到所有满足 \(r^d \equiv 1\pmod p\) 的整数 \(r\),随后对这些位置求解 \(f^{(d)}(r)\) 的值,祈祷着得到的值是 \(0\)
决定性的 trick 就是既然 \(f^{(d)}\) 的系数能被一劳永逸地算出来,我们就可以对大量质数分别在 \(O(d^2)\) 的复杂度内计算所有的 \(r\) 位置对应的值。
注意到,如果我们还相信 \(f^{(d)} \pmod p\) 的值是随机的(而且我们必须要相信),那大致上 \(\frac md\approx \frac {10^5}d\) 个质数就应当足够了。

还剩下两个问题:1. 我们该选什么样的数 \(d\)? 2. 我们该如何找到满足 \(r^d \equiv 1 \pmod p\) 的整数 \(r\)
对于 1. ,我们发现最好的选择就是那些小质数,例如 \(3,5,7,11,\cdots\)。这样我们就能留下大量形如 \(kd+1\) 的质数 \(p\),而且 \(O(d^2)\) 的时间复杂度开销也不会很大。
对于 2. ,当选好了 \(d\) 后找到满足如上条件的整数 \(r\) 也不是很难。我们随机一个整数 \(r_0\),取 \(r_0^{\frac{p-1}d}\) 为对应整数。正确性考虑费马小定理。这一步实际选出并在之后使用的数是 \(r_0^{\frac{p-1}d}\),因此其不可等于 \(1\)

具体地说,以下流程的算法跑得很好:

  1. 选择一个质数 \(d\) 以及一个质数 \(p = kd+1\)
  2. \(r\in \{1, 2, \dots, p-1\}\) 中随机抽取一个值。
  3. \(r^{\frac {p-1} d} \neq 1\) 进入下一步,反之回到上一步。
  4. 计算模 \(p\) 意义下的 \(f^{(d)}(r), f^{(d)}(r^2),\dots f^{(d)}(r^d)\)

可以证明,上述算法一定能够输出我们想要的答案。进一步的,由于每个 \(d\) 会产生 \(O(\frac md)\) 个质数 \(p\),每个质数 \(p\) 能够验证 \(O(d^2)\) 个数字,因此取得 \(d\) 但无法得到所需答案的概率为

\[\left(1-\frac 1p\right)^{d^2\times \frac pd} = \left(1-\frac 1p\right)^{pd} \]

可以看到期望复杂度很优秀。

code
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,a,b) for (register int i = (a), i##_ = (b) + 1; i < i##_; ++i)
#define pre(i,a,b) for (register int i = (a), i##_ = (b) - 1; i > i##_; --i)
const int N = 1e6 + 10;
int n, m, str[N];
char s[N], t[N]; 
 
int prime[N], cnt; bool vis[N];
void sieve(int bnd) {
    rep(i,2,bnd) {
        if (!vis[i]) prime[++cnt] = i;
        rep(j,1,cnt) {
            if (i * prime[j] > bnd) break;
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        } 
    }
}
 
int qp(int a, int b, int mod) {
    int ret = 1;
    while (b) {
        if (b & 1) ret = 1ll * ret * a % mod;
        a = 1ll * a * a % mod;
        b >>= 1;
    } return ret;
}
 
int a[N];
void check(int d) {
    rep(i,0,d-1) a[i] = 0;
    rep(i,0,n) a[i % d] += str[i];
    for (int p = m / d * d + d + 1; p < 1e6; p += d) if (!vis[p]) {
        int r, r0;
        while (true) {
            r = rand() * rand() % (p - 2) + 1;
            r0 = qp(r, (p-1) / d, p); 
            if (r0 == 1) continue;
            // cout << r << ' ' << r0 << endl;
            r = r0;
            rep(tmp,1,d) {
                int t = 0, bse = 1;
                rep(i,0,d-1) t = (t + 1ll * bse * a[i]) % p, bse = 1ll * bse * r % p;
                if (t == 0) { cout << p << ' ' << r << endl; exit(0); }
                r = 1ll * r0 * r % p;
            }
            break;
        }
    }
}
 
signed main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n >> m >> s+1 >> t+1;
    rep(i,1,n) str[i] = s[i] - t[i];
    sieve(1e6);
    rep(i,2,cnt) check(prime[i]);
}



CF1276D

给定一棵\(n\)个点的树,点编号\(1 \sim n\),第\(i\)条边连接\(a_i\)\(b_i\)。初始时你有一个空的序列,树上的\(n\)个点都有标记。现在按照边的编号从小到大考虑每一条边:

  • 如果这一条边连接的两个点都有标记,则选择其中的一个点,擦除它的标记并将它的编号放入序列的末端;

  • 否则什么都不做。

求能够由上述操作得到的不同的序列数量\(\bmod 998244353\)

\(n \le 2\times 10^5\)

dp 题。

由于加边是有顺序的,考虑按照时间设计 dp 数组状态。

我们称一条边覆盖了一个点,当且仅当这条边擦除了这个点的标记。
然后设 \(f_{i,j}\)\(i\) 节点子树的状态数,\(j\) 规定了 \(i\) 节点状态被擦除的情况。

  1. \(j=0\) 表示该节点被连向父亲的边前的连向儿子的边覆盖。
  2. \(j=1\) 表示该节点被连向父亲的边覆盖。
  3. \(j=2\) 表示该节点被连向父亲的边后的连向儿子的边覆盖。
  4. \(j=3\) 表示该节点没有被覆盖。

然后转移。

首先讨论被连向儿子的边覆盖。
枚举覆盖边连向的儿子。这个儿子 \(y\) 一定是在被连向父亲的边后被覆盖。转移值有 \(f_{y,2/3}\)。然后枚举其他儿子。对应边编号在 \(y\) 之前的点 \(z\) 一定要被覆盖,转移值有 \(f_{z,0/1}\),在 \(y\) 之后的点 \(z\) 不一定被覆盖,转移值有 $f_{z,0/2/3}。
先不考虑 \(j\) 的取值对 \(y\) 的限制,列出转移式:

\[f_{x,0/2} = \sum_{y} \left( (f_{y,2} + f_{y,3})\times \prod_{z < y} (f_{z,0} + f_{z,1}) \times \prod_{z \ge y} (f_{z,0} + f_{z,2}+f_{z,3}) \right) \]

限制自行加入。

然后讨论被父亲覆盖。
由于当前节点是被父亲覆盖的,因此其孩子中所有小于父亲的边都不能在覆盖当前节点后再覆盖。由于这条边不会删除当前节点,因此当前边只能擦除子节点。因此转移 \(f_{y,0/1}\)
相似的讨论说明对于大于父亲的节点不能选择 \(f_1\),因此转移 \(f_{y,0/2/3}\)

\[f_{x,1} = \prod_{z < fa} (f_{z,0} + f_{z,1}) \times \prod_{z \ge fa} (f_{z,0} + f_{z,2}+f_{z,3}) \]

然后讨论未被覆盖。

\[f_{x,3} = \prod_{y} (f_{z,0} + f_{z,1}) \]

转移即可。

code
#include <bits/stdc++.h>
using namespace std;
#ifdef ONLINE_JUDGE
    char buf[1<<21], *p1 = buf, *p2 = buf;  inline char getc() { return (p1 == p2 and (p2 = (p1 = buf) + fread(buf, 1, 1<<21, stdin), p1 == p2) ? EOF : *p1++); }
    #define getchar getc
#endif
template <typename T> inline void get(T & u){
	u = 0; char ch = getchar(); bool f = 0; while (ch < '0' or ch > '9') f = f or ch == '-',  ch = getchar();
	while (ch >= '0' and ch <= '9') u = (u<<1) + (u<<3) + (ch^48), ch = getchar(); f and (u = -u);
} template <typename T, typename ... Args> inline void get(T & u, Args & ... _Args) { get(u); get(_Args...); }
#define rep(i,a,b) for (register int i = (a), i##_ = (b) + 1; i < i##_; ++i)
#define pre(i,a,b) for (register int i = (a), i##_ = (b) - 1; i > i##_; --i)
const int N = 5e5 + 10, mod = 998244353;
int n, t1, t2, f[N][4]; 
vector <int> g[N];

void dfs(int u, int fa) {
    for (auto v : g[u]) if (v != fa) dfs(v, u);
    static int cnt, fap, _01[N], _23[N], _023[N];
    cnt = fap = 0; _01[0] = 1;
    for (auto v : g[u]) if (v == fa) fap = cnt;
    else {
        ++cnt;
        _01[cnt] = 1ll * _01[cnt - 1] * (f[v][0] + f[v][1]) % mod;
        _23[cnt] = f[v][2] + f[v][3]; if (_23[cnt] >= mod) _23[cnt] -= mod;
        _023[cnt] = _23[cnt] + f[v][0]; if (_023[cnt] >= mod) _023[cnt] -= mod;
    } 
    _023[cnt + 1] = 1;
    pre(i,cnt - 1,1) _023[i] = 1ll * _023[i + 1] * _023[i] % mod;
    rep(i,1,cnt) 
        if (i <= fap) f[u][0] = (f[u][0] + 1ll * _01[i - 1] * _23[i] % mod * _023[i + 1]) % mod;
        else f[u][2] = (f[u][2] + 1ll * _01[i - 1] * _23[i] % mod * _023[i + 1]) % mod;
    f[u][1] = 1ll * _01[fap] * _023[fap + 1] % mod;
    f[u][3] = _01[cnt];
}

signed main() {
    get(n); rep(i,2,n) get(t1, t2), g[t1].emplace_back(t2), g[t2].emplace_back(t1);
    dfs(1, 0);
    cout << (1ll * f[1][0] + f[1][2] + f[1][3]) % mod;
}

原文地址:http://www.cnblogs.com/joke3579/p/chitchat221028.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性