2020 520模擬賽 A.FWT優化 B.powerful_number求積性函數前綴和 C.毒瘤容斥狀壓(論文題:畫家小P)

A

在這裏插入圖片描述在這裏插入圖片描述
考試一看,60分白送 寫了走人。

發現可以FWTFWT。我們先轉化一下問題,把求最大的異或和爲00的集合轉化爲求最小的異或和爲CC的集合。其中C=i=1naiC=\oplus_{i=1}^na_i

那麼我們把每個aia_i位置加11,然後從小到大枚舉答案ii,卷積ii次後如果CC位置有值,那麼答案就是ii

然而由於線性基相關知識我們可以知道,如果能異或到CC,最多只需要logA\log A個數。而CC本來就是所有aia_i的異或和,所以答案ii一定在logA\log A範圍內。所以做log\log次卷積就行了。時間複雜度O(Alog2A)O(A\log^2A)。能過。

還可以優化到一個log\log,因爲我們只需要CC處的值,所以我們可以做一次FWT後,每次依照iFWTiFWT的定義,用O(A)O(A)來計算CC處的值。然後卷積一次就是O(A)O(A)個點值分別乘上一個數。總時間複雜度就是O(AlogA)O(A\log A)的。而且由於只需要判斷CC處是否非負,那麼依照FWTFWT的定義判斷是否非負也是可以的。

CODE

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 524288 + 5;
const int mod = 998244353;
int n, C, f[MAXN], g[MAXN], bit[MAXN];
inline int adj(int x) { return x < 0 ? x + mod : x >= mod ? x - mod : x; }
void FWT(int *a, int len) {
	for(int i = 2, u, v; i <= len; i<<=1)
		for(int j = 0; j < len; j += i)
			for(int k = j; k < j+i/2; ++k) {
				u = a[k], v = a[k+i/2];
				a[k] = adj(u + v);
				a[k+i/2] = adj(u - v);
			}
}
int main () {
	scanf("%d", &n); int len = 1;
	for(int i = 1, x; i <= n; ++i) {
		scanf("%d", &x), f[x] = 1, C ^= x;
		while(len <= x) len<<=1;
	}
	if(!C) { printf("%d\n", n); return 0; }
	bit[0] = 1;
	for(int i = 1; i < len; ++i) bit[i] = (i&1) ? mod-bit[i>>1] : bit[i>>1];
	FWT(f, len); memcpy(g, f, sizeof g);
	for(int i = 1; i <= n; ++i) {
		int sum = 0;
		for(int j = 0; j < len; ++j) sum = (sum + 1ll*bit[j&C]*f[j]) % mod; //直接按照FWT定義計算
		if(sum) { printf("%d\n", n-i); return 0; }                          //如果要求值,就再除以len,就得到了ifwt後的值
		for(int j = 0; j < len; ++j) f[j] = 1ll * f[j] * g[j] % mod;
	}
}

B

在這裏插入圖片描述在這裏插入圖片描述
直接min_25篩+O(k)算冪和只有40分。

這裏需要用一個叫做powerful_number的東西。詳見zjp-shadow的博客園,例子就是這道題。

我求冪和用的是拉格朗日差值求多項式係數。寫的有點長。其實可以每次預處理前綴後綴同樣也是O(k)O(k)求。或者用第二類斯特林數來求冪和。

第二類斯特林數公式:
nk=i=0k{ki}nin^k=\sum_{i=0}^k\begin{Bmatrix} k \\ i \end{Bmatrix}n^{\underline{i}}

i=1nik=i=0k{ki}(n+1)i+1i+1\sum_{i=1}^ni^k=\sum_{i=0}^k\begin{Bmatrix} k \\ i \end{Bmatrix}\frac{(n+1)^{\underline{i+1}}}{i+1}

CODE

#include <bits/stdc++.h>
using namespace std;
const int N = 1e7 + 1000;
const int mod = 1e9 + 7;
typedef long long LL;
LL n, a[N]; int K, m, sqr;
inline LL qpow(LL a, LL b) {
	LL re = 1;
	for(; b; b>>=1, a=a*a%mod)if(b&1)re=re*a%mod;
	return re;
}
LL f[25], g[25], y[25], inv[25][25];
LL calc(LL n) {
	if(n <= K+2) return y[n];
	LL re = 0, x = 1; n %= mod;
	for(int i = 0; i <= K+1; ++i) re = (re + f[i] * x) % mod, x = x * n % mod;
	return re;
}
int pr[N], cnt_pr; LL pk[N]; bool vis[N];
void pre() { //預處理1^k+2^k+...+n^k的k+1次多項式係數
	LL sum = 0;
	for(int i = 1; i <= K+2; ++i) {
		(sum += qpow(i, K)) %= mod; y[i] = sum;
		for(int j = 1; j <= K+2; ++j)
			if(i != j) inv[i][j] = qpow((i-j)%mod, mod-2);
	}
	for(int i = 1; i <= K+2; ++i) {
		memset(g, 0, sizeof g); g[0] = 1;
		LL cf = y[i];
		for(int j = 1; j <= K+2; ++j)
			if(i != j) {
				cf = cf * inv[i][j] % mod;
				for(int l = K+1; l >= 0; --l) {
					g[l] = g[l] * (mod-j) % mod;
					if(l) (g[l] += g[l-1]) %= mod;
				}
			}
		for(int j = 0; j <= K+1; ++j) {
			g[j] = g[j] * cf % mod;
			(f[j] += g[j]) %= mod;
		}
	}
	vis[0] = vis[1] = 1;
	for(int i = 2; i <= sqr; ++i) {
		if(!vis[i]) pr[++cnt_pr] = i, pk[cnt_pr] = qpow(i, K);
		for(int j = 1; j <= cnt_pr && pr[j]*i <= sqr; ++j) {
			vis[pr[j]*i] = 1;
			if(i % pr[j] == 0) break;
		}
	}
}
int id1[N], id2[N]; LL sum[N];
inline int &id(LL x) { return x <= sqr ? id1[x] : id2[n/x]; }
void dfs(LL x, int now, LL cf) {
	(sum[id(n/x)] += cf) %= mod;
	for(int i = now+1; i <= cnt_pr && x <= n/pr[i]/pr[i]; ++i) {
		LL y = pr[i], v = (pk[i] - pk[i]*pk[i]) % mod;
		do y *= pr[i], dfs(x*y, i, cf*v%mod);
		while(y <= n/x/pr[i]);
	}
}
int main () {
	cin >> n >> K; sqr = sqrt(n); pre();
	for(LL i = 1; i <= n; i = n/(n/i) + 1) a[id(n/i) = ++m] = n/i;
	dfs(1, 0, 1); LL ans = 0;
	for(int i = 1; i <= m; ++i)
		if(sum[i]) ans = (ans + sum[i] * calc(a[i])) % mod;
	cout << (ans+mod)%mod << endl;
}

C

在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述

CODE

論文題,詳見:IOI2019國家集訓隊第一階段作業 《畫家小P》解題報告 南京外國語學校 吳思揚

然後論文中給出了遞歸求nn個數分別滿足 0ailimiti0\le a_i\le limit_iai=C\oplus a_i=C 的方案。時間複雜度是O(n2logC)O(n^2\log C),其實可以做到O(8nlogC)O(8*n\log C)。具體做法如下:

下面的aia_i表示limitilimit_ibib_i表示第ii個位置實際取的值。
在這裏插入圖片描述在這裏插入圖片描述

論文講的做法錯綜複雜),其實寫出正解只需要論文裏的幾個部分做法拼在一起。可以看大佬博客截取了需要用的部分。

CODE

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 16;
const int MAXS = (1<<15)+5;
const int MAXS3 = 14348907 + 5;
const int mod = 998244353;
typedef long long LL;
int n, m, id[MAXN], rk[MAXN];
int dp[MAXS3], cf[MAXS], bit[MAXS], lg[MAXS], f[MAXS], st[MAXS];
LL C, a[MAXN];
bool fe[MAXS];
inline bool cmp(int i, int j) { return a[i] < a[j]; }
int inv2[65], pw3[MAXN];
int solve(int S) {
	static LL b[MAXN];
	static int g[MAXN][2][2];
	int cnt = 0; LL sum = 0;
	for(int i = 0; i < n; ++i) if(S>>i&1) b[cnt++] = a[i], sum ^= a[i];
	int re = sum == C;
	for(int k = 59; ~k; --k) {
		int chk = 0;
		for(int i = 0; i < cnt; ++i) chk ^= b[i]>>(k+1)&1;
		if(chk^(C>>(k+1)&1)) break;
		memset(g, 0, sizeof g); g[0][0][0] = 1;
		for(int i = 0, x; i < cnt; ++i) {
			x = b[i]>>k&1;
			for(int j = 0; j < 2; ++j)
				for(int v = 0; v < 2; ++v)
					for(int t = 0; t <= x; ++t)
						g[i+1][j^t][v|(t<x)] = (g[i+1][j^t][v|(t<x)] + (t == x ? (b[i]&((1ll<<k)-1))+1 : 1ll<<k) % mod * g[i][j][v]) % mod;
		}
		re = (re + 1ll * g[cnt][C>>k&1][1] * inv2[k]) % mod;
	}
	return re;
}
int main () {
	scanf("%d%d%lld", &n, &m, &C);
	for(int i = 0; i < n; ++i) scanf("%lld", &a[i]), lg[1<<i] = id[i] = i;
	sort(id, id + n, cmp);
	for(int i = 0; i < n; ++i) rk[id[i]] = i;
	sort(a, a + n);
	for(int i = 0, u, v; i < m; ++i) {
		scanf("%d%d", &u, &v), --u, --v;
		u = rk[u], v = rk[v];
		fe[(1<<u)|(1<<v)] = 1;
	}
	for(int s = 1; s < 1<<n; ++s)
		for(int i = 0; i < n; ++i)
			if(s>>i&1) fe[s] |= fe[s^1<<i];
	for(int s = 1; s < 1<<n; ++s) {
		cf[s] = !fe[s];
		for(int t = (s-1)&s; t; t=(t-1)&s) if(t&(s&-s))
			cf[s] = (cf[s] - cf[t] * (!fe[s^t])) % mod;
	}
	for(int s = 1; s < 1<<n; ++s)
		if(!((bit[s] = bit[s>>1] + (s&1))&1)) cf[s] = 1ll * cf[s] * (a[lg[s&-s]]%mod + 1) % mod;
	
	inv2[0] = 1, inv2[1] = (mod+1)/2; for(int i = 2; i <= 60; ++i) inv2[i] = 1ll * inv2[i-1] * inv2[1] % mod;
	f[0] = !C;
	for(int s = 1; s < 1<<n; ++s) f[s] = solve(s);
	
	pw3[0] = 1; for(int i = 1; i <= n; ++i) pw3[i] = 3 * pw3[i-1];
	for(int s = 1; s < 1<<n; ++s) st[s] = st[s>>1]*3 + (s&1);
	for(int s = 1; s < 1<<n; ++s) if(bit[s]&1) st[s] += pw3[lg[s&-s]];
	
	
	dp[0] = 1; int ans = 0, N = pw3[n];
	for(int s = 0; s < N; ++s) if(dp[s]) {
		int t = 0, lst = 0;
		for(int i = 0; i < n; ++i) if(s/pw3[i]%3 == 0) t |= 1<<i, (lst ? 0 : lst = 1<<i);
		if(t) {
			int rst = t ^ lst;
			for(int sub = rst; ; sub=(sub-1)&rst) {
				dp[s + st[sub|lst]] = (dp[s + st[sub|lst]] + 1ll * dp[s] * cf[sub|lst]) % mod;
				if(!sub) break;
			}
		}
		else {
			for(int i = 0; i < n; ++i) if(s/pw3[i]%3 == 2) t |= 1<<i;
			ans = (ans + 1ll * dp[s] * f[t]) % mod;
		}
	}
	printf("%d\n", (ans + mod) % mod);
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章