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);
}