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();
}
posted @ 2022-05-25 14:31  mizu164  阅读(102)  评论(0编辑  收藏  举报