闲话 22.11.5
闲话
调了个多项式板子 跑得飞快
似乎是oi界顶尖科技水平了
所以又发了柚子
srds 一天真的吃不完(
srds “怎么打开?” “树怎么剖的它就怎么剖的”
然后开了(
比较的甜 但是皮是真的不好吃
柚子但不是柚子社呢
下次回去玩一玩柚子社的游戏吧
哪位能给我赞助一份千恋万花(
为啥 lg 上我自己看不到我自己的新头像啊
雨のち曇り晴れ
優しくきっと晴れ
きらきら浮かぶ言葉
もう忘れちゃうけれど
口胡题了(
某些懒得写代码的题
在这一并解决了算了
给定一个长度为 \(n\) 的序列 \(a_1,\cdots,a_n\),你可以每次把两个相等 \(a_i\) 和 \(a_j\) 扔掉,加进来一个 \(a_i+1\)。 给定 \(q\) 次修改,每次将 \(a_k\) 修改为 \(l\),求每次操作后可能出现的最大数。(经过操作后的)
\(1\le n,q,a_i\le 2\times 20^5\)。
每个数最后一定出现不超过一次,反之一定不优。
然后考虑一个 01 串,第 \(i\) 位记录 \(i\) 在最终合并完后是否存在。
考虑这个合并等于是在进位。
然后更新就很好更新了。拆成彼此镜像的删除和加入,只讨论加入,删除反转 01 即可。
加入 \(l\) 时找到 \(l\) 后面第一个 0,把这俩之间的所有 1 置零,再把这一位置 1。
询问时输出最高位即可。
线段树即可。但是珂朵莉树的复杂度似乎是更优的。记得推平。
因为是 cf,可以 bitset 用 _Find_next() 卡过去。
对于一个数组 \(X\),有如下操作:对于区间 \([l,r]\),给 \(X_l\) 加上 \(F_1\),给 \(X_{l+1}\) 加上 \(F_2\),以此类推,并且给 \(X_r\) 加上 \(F_{r-l+1}\)。然后将区间 \([l,r]\) 内每个数对 \(MOD\) 取模。\(F\) 数组是这样一个数组:\(F_1=1\),\(F_2=1\),当 \(i>2\) 时 \(F_i=F_{i-1}+F_{i-2}\)。
已知两个长度相同的数组 \(A\),\(B\),给出若干次操作,每次操作后你需要求出取模过后的数组 \(A\),\(B\) 是否相等。
\(1\le n,q\le 3\times 10^5, 2 \le MOD \le 10^9 + 7\)。
判相等转化成差值为 \(0\)。然后我们只需要维护 \(C_i = A_i - B_i\)。对两个序列的操作可以被转化成对其中一个的操作。
然后考虑在 \(C_i\) 上加入 \(F\) 序列的做法。
如果加入的是递推式确定的值,可以考虑维护与递推式互逆的差分数组使得操作维度减小一维。
在这里考虑维护 \(D_{i} = C_i - C_{i-1} - C_{i-2}\),加和时只需要在 \(D_l\) 处 \(+1\) 即可在后缀上加,在 \(D_{r+1}\) 处 \(-F_{r-l+1}\),在 \(D_{r+2}\) 处 \(-F_{r-l+2}\) 即可抵消。
判断时直接判 \(D\) 是否全 \(0\) 即可。
代码简单。
一开始给定 \(n\) 个长度不一的正整数序列,编号为 \(1 \sim n\),初始序列可以为空。这 \(n\) 个序列被视为存在,其他编号对应的序列视为不存在。
有 \(q\) 次操作,操作有以下类型:
- \(1 \ x \ y\):在 \(x\) 号序列末尾插入数字 \(y\)。保证 \(x\) 号序列存在,且 \(1 \le x, y \le n + q\)。
- \(2 \ x\):删除 \(x\) 号序列末尾的数字,保证 \(x\) 号序列存在、非空,且 \(1 \le x \le n + q\)。
- \(3 \ m \ x_1 \ x_2 \ x_m\):将 \(x_1, x_2, \ldots, x_m\) 号序列顺次拼接,得到一个新序列,并询问其众数。如果不存在满足上述条件的数,则返回 \(-1\)。数据保证对于任意 \(1 \le i \le m\),\(x_i\) 是一个仍然存在的序列,\(1 \le x_i \le n + q\),且拼接得到的序列非空。注意:不保证 \({x_1, \ldots, x_m}\) 互不相同,询问中的合并操作不会对后续操作产生影响。
- \(4 \ x_1 \ x_2 \ x_3\):新建一个编号为 \(x_3\) 的序列,其为 \(x_1\) 号序列后顺次添加 \(x_2\) 号序列中数字得到的结果,然后删除 \(x_1, x_2\) 对应的序列。此时序列 \(x_3\) 视为存在,而序列 \(x_1, x_2\) 被视为不存在,在后续操作中也不会被再次使用。保证 \(1 \le x_1, x_2, x_3 \le n + q\)、\(x_1 \ne x_2\)、序列 \(x_1, x_2\) 在操作前存在、且在操作前没有序列使用过编号 \(x_3\)。
\(1\le n,q\le 5\times 10^5\),输入序列总长度 \(\le 5\times 10^5\),所有 3. 操作中出现的序列个数 \(\le 5\times 10^5\)。
受害者发言
根据死者的发言,这题的做法是维护摩尔投票合并。
我们直接开 \(n+q\) 个 std::list
,每个维护一个序列。
我们直接开 \(n+q\) 棵线段树,每个维护一个序列的元素个数。
线段树上推信息时按摩尔投票法,最后根节点的值就是这个序列中的绝对众数。
-
- 操作直接维护,单次复杂度 \(O(\log n)\)。
- 操作取得序列根位置的绝对众数,然后在新序列上跑摩尔投票。投出的众数再去原序列里查一遍。因为保证该操作中的序列个数 \(\le 5\times 10^5\),复杂度均摊正确。
- 操作启发式合并
std::list
即可。线段树合并的复杂度也是均摊正确。
为什么不用 std::deque
然后启发式合并?
那玩意预留空间太大,会 MLE。
进行了一波 k 叉分治 fft 的实现
code
#include <bits/stdc++.h>
using namespace std;
#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 = 3e5 + 10, mod = 998244353, inv2 = (mod + 1) >> 1, g = 3, B = 5;
using ull = unsigned long long;
using poly = vector<int> ;
int n;
int limit, trs[N], w[N], lgv[N], inv[N];
poly F, G;
int qp(int a, int b) { int ret = 1; while (b) { if (b & 1) ret = 1ll * ret * a % mod; a = 1ll * a * a % mod; b >>= 1; } return ret; }
void init(int n) {
limit = 1; while (limit < n) limit <<= 1;
inv[0] = inv[1] = 1;
for (int i = 1; i < limit; ++ i) trs[i] = (trs[i >> 1] >> 1) | (( i & 1 ) ? limit >> 1 : 0);
for (int i = 1; i < limit; i <<= 1) {
w[i] = 1; int w0 = qp(g, (mod - 1) / (i << 1));
rep(j,1,i-1) w[i + j] = 1ll * w[i + j - 1] * w0 % mod;
} rep(i,2,limit) lgv[i] = lgv[i >> 1] + 1, inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
}
void DFT(poly & A, int len) {
int t = lgv[limit / len]; static ull tmp[N];
rep(i,0,len-1) tmp[trs[i] >> t] = A[i];
for (int i = 1; i < len; i <<= 1) for (int j = 0; j < len; j += i << 1) for (int k = 0; k < i; ++ k) {
int v = 1ll * tmp[i + j + k] * w[i + k] % mod;
tmp[i + j + k] = tmp[j + k] + mod - v; tmp[j + k] += v;
} for (int i = 0; i < len; ++ i) A[i] = tmp[i] % mod;
}
void IDFT(poly & A, int len) {
reverse(A.begin()+1, A.begin()+len); DFT(A, len);
int iv = mod - (mod - 1) / len;
for (int i = 0; i < len; ++ i) A[i] = 1ll * A[i] * iv % mod;
}
poly _f[20][1 << B], _g[20][1 << B];
void dac_FFT(poly & F, const poly & G, int l, int r, int dep, void plain(poly&,const poly&,int,int)) {
if (r - l + 1 <= 128) { plain(F, G, l, r); return; }
static poly tmp; tmp.resize(0), tmp.resize(N);
int d = 1 << ((dep - 1) * B);
for (int i = 0; ; ++ i) {
int L = l + i * d, R = min(r, L + d - 1);
if (i) {
// memset(tmp.begin(), 0, (d << 3));
fill(tmp.begin(), tmp.begin() + (d << 1), 0);
for (int j = 0; j < i; ++ j) for (int k = 0; k < (d << 1); ++ k)
tmp[k] = (tmp[k] + 1ll * _f[dep][j][k] * _g[dep][i-j][k]) % mod;
IDFT(tmp, d << 1);
rep(j,L,R) F[j] = (F[j] + tmp[j - L + d]) % mod;
}
dac_FFT(F, G, L, R, dep - 1, plain);
if (R == r) return;
// memset(_f[dep][i].begin(), 0, (d << 3));
fill(_f[dep][i].begin(), _f[dep][i].begin() + (d << 1), 0);
copy(F.begin()+L, F.begin()+R+1, _f[dep][i].begin());
DFT(_f[dep][i], d << 1);
}
}
void dac(poly & F, const poly & G, int n, void plain(poly&,const poly&,int,int)) {
// memset(F.begin(), 0, (n << 2));
fill(F.begin(), F.begin()+n, 0);
if (n <= 128) { plain(F, G, 0, n-1); return; }
int len = 1, dep = 0;
while (len < n) len <<= B, dep ++; len >>= B;
rep(i,1,dep) {
int d = 1 << ((i-1) * B), tn = min((1 << B) - 1, (n - 1) / d);
rep(j,1,tn) {
int L = (j - 1) * d + 1, R = min(n - 1, (j + 1) * d - 1);
_f[i][j-1].resize(d << 1);
_g[i][j].resize(d << 1);
// memset(_g[i][j].begin(), 0, (d << 3));
fill(_g[i][j].begin(), _g[i][j].begin() + (d << 1), 0);
copy(G.begin()+L, G.begin()+R+1, _g[i][j].begin() + 1);
DFT(_g[i][j], d << 1);
}
}
dac_FFT(F, G, 0, n-1, dep, plain);
}
void plain_inv(poly & F, const poly & G, int l, int r) {
if (!l) F[l] = 1;
rep(i,l+1,r) rep(j,l,i-1) F[i] = (F[i] + 1ll * F[j] * G[i - j]) % mod;
}
void plain_ln(poly & F, const poly & G, int l, int r) {
if (!l) F[l] = 0; else F[l] = (1ll * l * G[l] - F[l] + mod) % mod;
rep(i,l+1,r) {
rep(j,l,i-1) F[i] = (F[i] + 1ll * F[j] * G[i - j]) % mod;
F[i] = (1ll * i * G[i] - F[i] + mod) % mod;
}
}
void plain_exp(poly & F, const poly & G, int l, int r) {
if (!l) F[l] = 1; else F[l] = 1ll * F[l] * inv[l] % mod;
rep(i,l+1,r) {
rep(j,l,i-1) F[i] = (F[i] + 1ll * F[j] * G[i - j]) % mod;
F[i] = 1ll * F[i] * inv[i] % mod;
}
}
void Ln(poly & Ret, const poly & Inp, const int & n) {
dac(Ret, Inp, n, plain_ln);
Ret[0] = 0; rep(i,1,n-1) Ret[i] = 1ll * Ret[i] * inv[i] % mod;
}
void Exp(poly & Ret, const poly & Inp, const int & n) {
static poly _G; _G.resize(n);
rep(i,0,n-1) _G[i] = 1ll * i * G[i] % mod;
dac(F, _G, n, plain_exp);
}
void Inv(poly & Ret, const poly & Inp, const int & n) {
static poly _G; _G.resize(n);
int ivp = qp(G[0], mod - 2);
rep(i,0,n-1) _G[i] = 1ll * (mod - G[i]) * ivp % mod;
dac(Ret, _G, n, plain_inv);
rep(i,0,n-1) F[i] = 1ll * F[i] * ivp % mod;
}
void Pow(poly & Ret, const poly & Inp, const int & C, const int & n) {
static poly _R; _R.resize(0), _R.resize(n);
Ln(_R, Inp, n);
for (int i = 0; i < n; ++ i) _R[i] = 1ll * _R[i] * C % mod;
Exp(Ret, _R, n);
}
signed main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n; init(n); F.resize(n), G.resize(n);
for (int i = 0; i < n; ++ i) cin >> G[i];
Exp(F, G, n);
for (int i = 0; i < n; ++ i) cout << F[i] << ' ';
}
以下是博客签名,与正文无关。
请按如下方式引用此页:
本文作者 joke3579,原文链接:https://www.cnblogs.com/joke3579/p/chitchat221105.html。
遵循 CC BY-NC-SA 4.0 协议。
请读者尽量不要在评论区发布与博客内文完全无关的评论,视情况可能删除。