牛客挑战赛36 G Nim游戏(分治FWT)

https://ac.nowcoder.com/acm/contest/3782/G

题解:

分治FWT裸题。

每个都相当于\((1+b[i]x^{a[i]})\),求这玩意的异或卷积。

先把a[i]相同的并在一起。

考虑分治,一个区间内的数的二进制的前若干位是相同的,所以只需要记录这个区间的数选了奇数还是偶数个以及后面的二进制位每一个异或结果的系数。

考虑合并,两个子区间二进制位上只有一个不同,那么右区间用了奇数个的话,这一位+1就好了。

写递归似乎常数比较大。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

#define gc getchar
template<class T> void read(T &x) {
	char c = ' '; x = 0;
	while(c < '0' || c > '9') c = gc();
	for(; c >= '0' && c <= '9'; c = gc()) x = x * 10 + c - '0';
}

const int mo = 998244353;

ll ksm(ll x, ll y) {
	ll s = 1;
	for(; y; y /= 2, x = x * x % mo)
		if(y & 1) s = s * x % mo;
	return s;
}

const int N = 131072;


int n, x, y;
ll f[N][2], a[N], b[N];

void fwt(ll *a, int n, int f) {
	ll b;
	for(int i = 1; i < n; i *= 2) for(int j = 0; j < n; j += 2 * i) ff(k, 0, i)
		b = a[i + j + k], a[i + j + k] = a[j + k] - b, a[j + k] += b;
	ff(i, 0, n) a[i] %= mo;
	if(f == -1) {
		b = ksm(n, mo - 2);
		ff(i, 0, n) a[i] = a[i] * b % mo;
	}
}

ll g[N][2];

void dg(int x, int y) {
	if(x == y) return;
	int m = x + y >> 1;
	dg(x, m); dg(m + 1, y);
	int n = y - m;
	fo(k, x, y) g[k][0] = g[k][1] = 0;
	fo(i, 0, 1) fo(j, 0, 1) {
		fo(k, x, m) a[k - x] = f[k][i];
		fo(k, m + 1, y) b[k - (m + 1)] = f[k][j];
		fwt(a, n, 1); fwt(b, n, 1);
		ff(k, 0, n) a[k] = a[k] * b[k] % mo;
		fwt(a, n, -1);
		ff(k, 0, n) g[x + k + j * (x ^ (m + 1))][i ^ j] += a[k];
	}
	fo(k, x, y) f[k][0] = g[k][0] % mo, f[k][1] = g[k][1] % mo;
}

int main() {
	scanf("%d", &n);
	ff(i, 0, N) f[i][0] = 1;
	fo(i, 1, n) {
		read(x); read(y);
		ll f0 = f[x][0];
		f[x][0] = (f[x][0] + f[x][1] * y) % mo;
		f[x][1] = (f[x][1] + f0 * y) % mo;
	}
	dg(0, N - 1);
	ll ans = (f[0][0] + f[0][1] - 1 + mo + mo) % mo;
	pp("%lld\n", ans);
}
posted @ 2020-02-27 21:29  Cold_Chair  阅读(533)  评论(0编辑  收藏  举报