UOJ743 【ZJOI2022】面条 【观察,FFT】
给定长为 \(n\) 的非负整数序列 \(a_1,\cdots,a_n\) 和正整数 \(x\),每次操作同时令 \(a_i:=a_{\lceil i/2\rceil}+a_{n-\lceil i/2\rceil+1}\)。
\(q\) 次询问非负整数 \(k\) 表示求 \(k\) 次操作后的 \(2^{-k}a_x\bmod 998\,244\,353\) 的值。
\(T\le 10\) 组数据,\(\sum n\le 2\cdot 10^6\),\(\sum q\le 5\cdot 10^7\),\(k\le 10^{18}\),\(x\le n\),\(a_i<998\,244\,353\),\(2\mid n\),\(k\) 独立均匀随机生成。
注意到操作一次之后 \(a_{2i-1}=a_{2i}\),设 \(k=\nu_2(n)\),打表可知操作 \(i\ge k+1\) 次后变为 \(m=\lfloor n/2^{k+1}\rfloor\) 个数分别重复 \(2^{k+1}\) 次,最后 \(1\) 个数重复 \(2^k\) 次。
设这些数是 \(c_0,\cdots,c_m\),则操作后变为 \(c_0+c_m,c_0+c_{m-1},c_1+c_{m-1},\cdots,2c_{\lfloor m/2\rfloor}\)。
设差分数组是 \(d_1,\cdots,d_m\),则操作后变为 \(-d_m,d_1,\cdots,(-1)^md_{\lceil m/2\rceil}\),可以看作 \((d_1,d_2,\cdots,d_m,-d_m,-d_{m-1},\cdots,-d_1)\) 的置换,其将位置 \(i\) 映至 \(2i\bmod(2m+1)\),从而每个循环长度都是 \(r=\text{ord}_{2m+1}(2)\) 的因数。
然后我们还已知所有元素之和 \(S\) 每次变为 \(2\) 倍,解方程可知 \(2^{-k}c_x=\frac S{2m+1}+2^{-k}\sum_{i=1}^m(\frac{2i}{2m+1}-[i>x])d_{2^{-k}i}\)。
对于后面这个和式,可以对每个循环用卷积预处理出每个 \(k\) 的答案,时间复杂度 \(\mathcal O(n\text d(n)+q)\)。
#include<bits/stdc++.h>
#define fi first
#define se second
typedef long long LL;
typedef unsigned long long ULL;
typedef std::pair<int, int> pii;
const int N = 1 << 21, M = 1 << 15, mod = 998244353;
int pw2[M + 1], pw2b[M];
int pwi(int x){return (LL)pw2b[x >> 15] * pw2[x & (M - 1)] % mod;}
int ksm(int a, int b){
int res = 1;
for(;b;b >>= 1, a = (LL)a * a % mod)
if(b & 1) res = (LL)res * a % mod;
return res;
}
void qmo(int &x){x += x >> 31 & mod;}
int test, T;
ULL seed;
ULL rd(){return seed ^= (seed << 13), seed ^= (seed >> 7), seed ^= (seed << 17);}
int n, q, x, a[N], ans[25], hah[N], ord;
LL kmax;
void work(){
static int b[N >> 1];
for(int i = 0;i < (n >> 1);++ i) qmo(b[i] = a[i] + a[n - i - 1] - mod);
for(int i = 0;i < (n >> 1);++ i) a[i << 1] = a[i << 1 | 1] = b[i];
}
int lim, rev[N << 1], w[2][N << 1];
void calrev(int len){
int L = -1; lim = 1; while(lim <= len){lim <<= 1; ++ L;}
for(int i = 1;i < lim;++ i) rev[i] = rev[i >> 1] >> 1 | ((i & 1) << L);
}
void NTT(int *A, bool op){
for(int i = 0;i < lim;++ i)
if(i < rev[i]) std::swap(A[i], A[rev[i]]);
for(int md = 1;md < lim;md <<= 1)
for(int i = 0;i < lim;i += md << 1)
for(int j = 0;j < md;++ j){
int y = (LL)A[i + j + md] * w[op][md + j] % mod;
qmo(A[i + j + md] = A[i + j] - y);
qmo(A[i + j] += y - mod);
}
if(op){
int iv = ksm(lim, mod - 2);
for(int i = 0;i < lim;++ i) A[i] = (LL)A[i] * iv % mod;
}
}
int A[N << 1], B[N << 1], tmp[N];
std::vector<pii> cyc;
bool vis[N];
void solve(){
cyc.clear();
std::cin >> n >> q >> x >> kmax; -- x;
for(int i = 0;i < n;++ i) std::cin >> a[i];
int lg = __builtin_ctz(n);
for(int i = 0;i <= lg;++ i){
ans[i] = a[x]; work();
}
ans[lg + 1] = a[x];
int m = n >> (lg + 1);
LL Ans = 0;
if(!m){
for(int i = 1;i <= q;++ i){
LL k = rd() % kmax;
if(k > lg) k = lg;
Ans ^= (LL)ans[k] * pwi(k) % mod * i;
}
std::cout << Ans << '\n';
return;
}
x >>= lg + 1;
for(int i = 0;i <= m;++ i) a[i] = a[i << (lg + 1)];
int avg = 0;
for(int i = 0;i < m;++ i) qmo(avg += a[i] - mod);
int iv = ksm(m << 1 | 1, mod - 2);
avg = (2ll * avg + a[m]) * iv % mod * pwi(lg + 1) % mod;
for(int i = m;i;-- i){
qmo(a[i] -= a[i - 1]);
qmo(a[2 * m - i + 1] = -a[i]);
}
int md = m << 1 | 1;
memset(vis, 0, md);
for(int i = 1;i < md;++ i) if(!vis[i]){
vis[i] = true;
int l = 1, x = i << 1;
if(x >= md) x -= md;
while(x != i){
vis[x] = true; ++ l;
x <<= 1; if(x >= md) x -= md;
}
cyc.emplace_back(l, i);
}
sort(cyc.begin(), cyc.end());
ord = cyc.back().fi;
memset(hah, 0, ord << 2);
for(int i = 0, j = 0;i < (int)cyc.size();i = j){
int len = cyc[i].fi;
memset(tmp, 0, len << 2);
while(j < (int)cyc.size() && len == cyc[j].fi) ++ j;
calrev(len << 1);
for(int $ = i;$ < j;++ $){
int now = cyc[$].se;
memset(A, 0, lim << 2);
memset(B, 0, lim << 2);
for(int k = 0;k < len;++ k){
if(now <= m) A[k] = 2ll * now * iv % mod - (now > x);
B[len - k] = a[now];
now <<= 1; if(now >= md) now -= md;
}
NTT(A, 0); NTT(B, 0);
for(int k = 0;k < lim;++ k) A[k] = (LL)A[k] * B[k] % mod;
NTT(A, 1);
for(int k = 0;k < len;++ k){
qmo(tmp[k] += A[k] - mod);
qmo(tmp[k] += A[k + len] - mod);
}
}
for(int $ = 0;$ < ord;$ += len)
for(int k = 0;k < len;++ k)
qmo(hah[$ + k] += tmp[k] - mod);
}
for(int i = 1;i <= q;++ i){
LL k = rd() % kmax;
if(k <= lg + 1) Ans ^= (LL)ans[k] * pwi(k) % mod * i;
else Ans ^= (avg + (LL)hah[(k - lg - 1) % ord] * pwi(k % (mod - 1))) % mod * i;
}
std::cout << Ans << '\n';
}
int main(){
std::ios::sync_with_stdio(0);
std::cin >> test >> T >> seed; *pw2 = *pw2b = 1;
for(int i = 1;i <= M;++ i) pw2[i] = (pw2[i - 1] + (pw2[i - 1] & 1) * mod) >> 1;
for(int i = 1;i < M;++ i) pw2b[i] = (LL)pw2b[i - 1] * pw2[M] % mod;
for(int md = 1;md <= N;md <<= 1){
int Wn = ksm(3, (mod - 1) / (md << 1));
w[0][md] = w[1][md] = 1;
for(int i = 1;i < md;++ i) w[0][md + i] = (LL)w[0][md + i - 1] * Wn % mod;
for(int i = 1;i < md;++ i) w[1][md + i] = mod - w[0][(md << 1) - i];
}
while(T --) solve();
}