A
考試一看,60分白送 寫了走人。
發現可以。我們先轉化一下問題,把求最大的異或和爲的集合轉化爲求最小的異或和爲的集合。其中。
那麼我們把每個位置加,然後從小到大枚舉答案,卷積次後如果位置有值,那麼答案就是。
然而由於線性基相關知識我們可以知道,如果能異或到,最多只需要個數。而本來就是所有的異或和,所以答案一定在範圍內。所以做次卷積就行了。時間複雜度。能過。
還可以優化到一個,因爲我們只需要處的值,所以我們可以做一次FWT後,每次依照的定義,用來計算處的值。然後卷積一次就是個點值分別乘上一個數。總時間複雜度就是的。而且由於只需要判斷處是否非負,那麼依照的定義判斷是否非負也是可以的。
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的博客園,例子就是這道題。
我求冪和用的是拉格朗日差值求多項式係數。寫的有點長。其實可以每次預處理前綴後綴同樣也是求。或者用第二類斯特林數來求冪和。
第二類斯特林數公式:
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》解題報告 南京外國語學校 吳思揚
然後論文中給出了遞歸求個數分別滿足 且 的方案。時間複雜度是,其實可以做到。具體做法如下:
下面的表示,表示第個位置實際取的值。
論文講的做法錯綜複雜),其實寫出正解只需要論文裏的幾個部分做法拼在一起。可以看大佬博客截取了需要用的部分。
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);
}