2022/02/08 模拟赛题解
蚌埠住了,挂了 \(75\%\) 的分。
T1
Description
给出一个序列,分成若干段,把每段最大值取出来作为 \(B_{1,2,...,n}\),使 \(\sum_{i=2} ((B_i-B_{i-1})^2+C)\) 最大。
\(n\le 10^6\)
Solution
考试的时候傻了,写了一个线段树套李超树,结果自己乱改没测大样例交上去挂完了。/kk
不难发现,假如对于一个点它能作为最大值的最大区间为 \([\text{col}_i,\text{cor}_i]\),那么我们可以设 \(f_i\) 表示前面 \(i\) 个考虑选了 \(i\) 的最大值,那么我们存在转移式:
至此,我们就可以线段树套李超树做到 \(\Theta(n\log^2n)\) 。不过我们发现我们其实可以去掉限制条件,因为如果中间有更大值一定加入这个更优。所以可以直接李超树 \(\Theta(n\log n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define ll long long
#define MAXM 1000005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}
ll C,f[MAXM];
int sqr (int x){return x * x;}
int n,a[MAXM],col[MAXM],cor[MAXM];
int cnt,rt,tmp[MAXM];
struct LiChao{
#define ls(x) son[x][0]
#define rs(x) son[x][1]
int cnt,son[15000005][2];ll tk[15000005],tb[15000005];
void modify (int &x,int l,int r,ll k,ll b){//表示在[l,r]中插入kx+b这条线段
if (!x) x = ++ cnt;
int mid = l + r >> 1;ll vnow = tmp[mid] * k + b,vlst = tmp[mid] * tk[x] + tb[x];
if (!tk[x]){tk[x] = k,tb[x] = b;return ;}
if (l == r){
if (vnow > vlst) tk[x] = k,tb[x] = b;
return ;
}
if (k < tk[x]){
if (vnow < vlst) modify (ls(x),l,mid,k,b);
else modify (rs(x),mid + 1,r,tk[x],tb[x]),tk[x] = k,tb[x] = b;
}
else{
if (vnow < vlst) modify (rs(x),mid + 1,r,k,b);
else modify (ls(x),l,mid,tk[x],tb[x]),tk[x] = k,tb[x] = b;
}
}
ll query (int x,int l,int r,int t){
if (!tk[x]) return -2e18;
ll now = tk[x] * tmp[t] + tb[x],mid = l + r >> 1;
if (t <= mid) chkmax (now,query (ls(x),l,mid,t));
else chkmax (now,query (rs(x),mid + 1,r,t));
return now;
}
}tree;
signed main(){
freopen ("array.in","r",stdin);
freopen ("array.out","w",stdout);
read (n),read (C);
for (Int i = 1;i <= n;++ i) read (a[i]);
for (Int i = 1;i <= n;++ i) tmp[i] = a[i];
sort (tmp + 1,tmp + n + 1),cnt = unique (tmp + 1,tmp + n + 1) - tmp - 1;
for (Int i = 1;i <= n;++ i) a[i] = lower_bound (tmp + 1,tmp + cnt + 1,a[i]) - tmp;
for (Int i = 1;i <= n;++ i){
if (i == 1) f[i] = 0;
else f[i] = tree.query (rt,1,cnt,a[i]) + 1ll * tmp[a[i]] * tmp[a[i]] + C;
tree.modify (rt,1,cnt,-2 * tmp[a[i]],f[i] + 1ll * tmp[a[i]] * tmp[a[i]]);
}
write (f[n]),putchar ('\n');
return 0;
}
T2
Description
一个长度为 \(n\) 的序列,可以查 \(2\) 次位置上的值,或是 \(30\) 次查询位置集合中两两差的绝对值集合。\(n\le 250\)
Solution
不难发现我们可以先查一次全集,可以知道极差,那么我们就可以二分出位置较大的最小值或是最大值 \(x\)。我们考虑对于二进制下的每一位集合,查该集合以及该集合并上 \(x\) 的差的绝对值集合,就可以知道他们与 \(x\) 的差值。又因为每个值两两不同,所以对于一个值,最多每个位置查询出现一次,所以我们就可以确定位置。我们还可以找到另一个最值,查一下就知道 \(x\) 是最大还是最小了。查询次数 \(3\log n\)。
Code
#include "difference.h"
#include <bits/stdc++.h>
using namespace std;
#define Int register int
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}
void find(int n, int M1, int M2){
set <int> Spos[25];
vector<int> S, ret;ret.resize (n);
int l = 1,r = n,ans = 0;
for (Int i = 0;i < n;++ i) S.push_back (i);
vector<int> H = qry2 (S);int mxv = 0;
for (Int v : H) chkmax (mxv,v);
while (l <= r){
int mid = l + r >> 1;
S.clear ();for (Int j = 1;j <= mid;++ j) S.push_back (j - 1);
H = qry2 (S);int maxv = 0;for (Int v : H) chkmax (maxv,v);
if (mxv == maxv) ans = mid,r = mid - 1;
else l = mid + 1;
}
for (Int i = 0;(1 << i) <= n;++ i){
S.clear ();
for (Int x = 1;x <= n;++ x) if (x >> i & 1) if (x != ans) S.push_back (x - 1);
vector<int> H1 = qry2 (S);S.push_back (ans - 1);vector <int> H2 = qry2 (S);
sort (H1.begin(),H1.end()),sort (H2.begin(),H2.end());int s2 = 0;
for (Int s1 = 0;s1 < H1.size();++ s1){
while (H2[s2] != H1[s1]) Spos[i].insert (H2[s2]),s2 ++;
s2 ++;
}
while (s2 < H2.size()) Spos[i].insert (H2[s2]),s2 ++;
}
set <int> All;
for (Int i = 0;(1 << i) < n;++ i) for (Int v : Spos[i]) All.insert (v);
auto it = All.end();-- it;int v = (*it),y = 0;All.erase (it);
for (Int i = 0;(1 << i) <= n;++ i) if (Spos[i].find (v) != Spos[i].end()) y += (1 << i),Spos[i].erase (v);
ret[ans - 1] = qry1 (ans - 1),ret[y - 1] = qry1 (y - 1);
for (Int v : All){
int pos = 0;
for (Int i = 0;(1 << i) < n;++ i) if (Spos[i].find (v) != Spos[i].end()) pos += (1 << i),Spos[i].erase (v);
if (ret[ans - 1] < ret[y - 1]) ret[pos - 1] = ret[ans - 1] + v;
else ret[pos - 1] = ret[ans - 1] - v;
}
answer (ret);
}
T3
Description
给出集合 \(S,T\),问构造一个长度为 \(L\) 的序列 \(a\) 使得\(\forall i\in [1,L],s.t. a_i\in S\) 且任意前缀异或值 \(\notin T\)。
\(|S|\le 35,|T|\le 20,L\le 4000\)
Solution
不难发现我们可以直接容斥,只需要钦定若干位置属于 \(T\) 即可。那么我们可以直接设 \(\text{dp}_{i,j}\) 表示考虑前面 \(i\) 位,第 \(i\) 个是 \(T_j\) 的容斥贡献。
考虑如何计算一个长度从 \(S\) 中选使得异或和为 \(v\) 的方案数。我们可以先对 \(S\) 构造线性基,如果大小 \(\le n/2\),那么我们只需要计算:
然后对于每个 \(x_i\) 位置的 \(y_j\) 就表示异或和为 \(i\) 用了 \(j\) 个选奇数次的 \(s_k\),然后方案就是系数乘上 \(f_{l,j}\)。其中,\(f_{l,i}\) 表示长度为 \(l\) 的序列有 \(i\) 个选奇数次的值的方案数。这个玩意我们可以使用生成函数算出来,你发现假如有 \(k\) 个选奇数次,那就是:
可以直接展开,然后算对每一位的贡献。
对于线性基大小 \(>n/2\),可以发现不在线性基中的元素个数 \(\le n/2\),那么我们可以枚举他们选择奇偶性,就可以确定线性基里面的选择方案。
然后考虑如何优化 dp,我们发现如果我们把答案写成向量多项式 \(F\),转移写成矩阵多项式 \(G\),就有关系:
其中 \(T\) 的每一位就是从 \(0\) 号位直接转移的向量多项式。然后我们就可以直接求逆求了。实现的比较丑陋。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define mod 998244353
#define MAXN 4005
#define MAXM 45
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}
int n,m,L,s[MAXN],t[MAXN],fac[MAXN],ifac[MAXN],dp[MAXN][MAXM];
int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
int qkpow (int a,int b){
int res = 1;for (;b;b >>= 1,a = mul (a,a)) if (b & 1) res = mul (res,a);
return res;
}
void Add (int &a,int b){a = add (a,b);}
void Sub (int &a,int b){a = dec (a,b);}
int binom (int a,int b){return mul (fac[a],mul (ifac[b],ifac[a - b]));}
#define poly vector<int>
#define SZ(A) ((A).size())
#define MAXX 800005
void putout (poly A){
cout << SZ(A) << ": ";
for (Int i = 0;i < SZ(A);++ i) cout << A[i] << " , ";cout << endl;
}
int rev[MAXX],w[MAXX];
void init_ntt (){
int lim = 1 << 18;
for (Int i = 0;i < lim;++ i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << 17);
int Wn = qkpow (3,(mod - 1) / lim);w[lim >> 1] = 1;
for (Int i = lim / 2 + 1;i < lim;++ i) w[i] = mul (w[i - 1],Wn);
for (Int i = lim / 2 - 1;i;-- i) w[i] = w[i << 1];
}
void ntt (poly &a,int type){
#define G 3
#define Gi 332748118
static int d[MAXX];int lim = a.size();
for (Int i = 0,z = 18 - __builtin_ctz(lim);i < lim;++ i) d[rev[i] >> z] = a[i];
for (Int i = 1;i < lim;i <<= 1)
for (Int j = 0;j < lim;j += i << 1)
for (Int k = 0;k < i;++ k){
int x = mul (w[i + k],d[i + j + k]);
d[i + j + k] = dec (d[j + k],x),d[j + k] = add (d[j + k],x);
}
for (Int i = 0;i < lim;++ i) a[i] = d[i] % mod;
if (type == -1){
reverse (a.begin() + 1,a.begin() + lim);
for (Int i = 0,Inv = qkpow (lim,mod - 2);i < lim;++ i) a[i] = mul (a[i],Inv);
}
#undef G
#undef Gi
}
poly operator * (poly A,poly B){
int lim = 1,l = 0,len = SZ(A) + SZ(B) - 1;
while (lim < SZ(A) + SZ(B)) lim <<= 1,++ l;
A.resize (lim),B.resize (lim),ntt (A,1),ntt (B,1);
for (Int i = 0;i < lim;++ i) A[i] = mul (A[i],B[i]);
ntt (A,-1),A.resize (len);
return A;
}
poly operator ^ (poly A,poly B){
int l = 0,len = SZ(A) + SZ(B) - 1;poly C;C.resize (len);
for (Int i = 0;i < SZ(A);++ i)
for (Int j = 0;j < SZ(B);++ j) Add (C[i + j],mul (A[i],B[j]));
return C;
}
poly operator + (poly A,poly B){
int len = max (SZ(A),SZ(B));A.resize (len),B.resize (len);
for (Int i = 0;i < len;++ i) Add (A[i],B[i]);
return A;
}
poly operator - (poly A,poly B){
int len = max (SZ(A),SZ(B));A.resize (len),B.resize (len);
for (Int i = 0;i < len;++ i) A[i] = dec (A[i],B[i]);
return A;
}
poly operator * (poly A,int b){
for (Int i = 0;i < SZ(A);++ i) A[i] = mul (A[i],b);
return A;
}
int siz,ind[35],val[35],dfn[35],Siz[35];
void build_base (int v,int id){
int cnt = 0;
for (Int i = 30;~i;-- i)
if (v >> i & 1){
if (!ind[i]){
siz ++,dfn[i] = siz,val[i] = v,ind[i] = id,Siz[i] = cnt ^ (1 << i);
return ;
}
v ^= val[i],cnt ^= Siz[i];
}
}
void fwt (poly *f,int type){
for (Int R = 2,mid = 1;R <= (1 << siz);R <<= 1,mid <<= 1)
for (Int i = 0;i < (1 << siz);i += R)
for (Int j = 0;j < mid;++ j){
f[i + j] = f[i + j] + f[i + j + mid];
f[i + j + mid] = f[i + j] - f[i + j + mid] * 2;
f[i + j] = f[i + j] * type,f[i + j + mid] = f[i + j + mid] * type;
}
}
bool vis[MAXM];
poly f[MAXM][131075];int stp[MAXN][21][21];
struct Matrix{
int mat[21][21];
Matrix(){memset (mat,0,sizeof (mat));}
void clear(){memset (mat,0,sizeof (mat));}
Matrix operator * (const Matrix &p)const{
Matrix New;
for (Int i = 1;i <= m;++ i)
for (Int j = 1;j <= m;++ j)
for (Int k = 1;k <= m;++ k) Add (New.mat[i][k],mul (mat[i][j],p.mat[j][k]));
return New;
}
Matrix operator + (const Matrix &p)const{
Matrix New;
for (Int i = 1;i <= m;++ i)
for (Int j = 1;j <= m;++ j) New.mat[i][j] = add (mat[i][j],p.mat[i][j]);
return New;
}
Matrix operator - (const Matrix &p)const{
Matrix New;
for (Int i = 1;i <= m;++ i)
for (Int j = 1;j <= m;++ j) New.mat[i][j] = dec (mat[i][j],p.mat[i][j]);
return New;
}
Matrix operator * (const int &p)const{
Matrix New;
for (Int i = 1;i <= m;++ i)
for (Int j = 1;j <= m;++ j) New.mat[i][j] = mul (mat[i][j],p);
return New;
}
int * operator [] (int key){return mat[key];}
void putout (){
for (Int i = 1;i <= m;++ i){
for (Int j = 1;j <= m;++ j) cout << mat[i][j] << " ";
cout << endl;
}
}
}I;
#define Poly vector<Matrix>
poly Ts[21][21];
void NTT (Poly &a,int type){
int len = SZ(a);
for (Int i = 1;i <= m;++ i)
for (Int j = 1;j <= m;++ j){
Ts[i][j].resize (len);
for (Int k = 0;k < SZ(a);++ k) Ts[i][j][k] = a[k][i][j];
ntt (Ts[i][j],type);
for (Int k = 0;k < SZ(a);++ k) a[k][i][j] = Ts[i][j][k];
}
}
Poly operator * (Poly A,Poly B){
int lim = 1,l = 0,len = SZ(A) + SZ(B) - 1;
while (lim <= SZ(A) + SZ(B)) lim <<= 1,++ l;
A.resize (lim),B.resize (lim),NTT (A,1),NTT (B,1);
for (Int i = 0;i < lim;++ i) A[i] = A[i] * B[i];
NTT (A,-1),A.resize (len);
return A;
}
Poly inv (Poly a,int N){
Poly b,c;b.resize (1);
for (Int i = 1;i <= m;++ i) b[0][i][i] = 1;
for (Int l = 4;(l >> 2) < N;l <<= 1){
c.resize (l >> 1);
for (Int i = 0;i < (l >> 1);++ i) if (i < N) c[i] = a[i];else c[i].clear();
c.resize (l),b.resize (l),NTT (c,1),NTT (b,1);
for (Int i = 0;i < l;++ i) b[i] = b[i] * (I * 2 - c[i] * b[i]);
NTT (b,-1),b.resize (l >> 1);
}
return b;
}
Poly inv (Poly a){return inv (a,SZ(a));}
int g[MAXN][MAXM],pw[MAXN << 1];
int upd (int x){return x < 0 ? x + mod : x;}
void init (){
int inv2 = (mod + 1) >> 1,iv = qkpow (inv2,n);
for (Int k = 0;k <= n;++ k){
for (Int j = -n;j <= n;++ j) pw[n + j] = 1;
for (Int l = 0;l <= L;++ l){
for (Int i = 0;i <= n - k;++ i)
for (Int j = 0;j <= k;++ j){
int v = mul (binom (n - k,i),binom (k,j));
if (k - j & 1) v = mod - v;
Add (dp[l][k],mul (pw[2 * (i + j)],mul (v,iv)));
}
for (Int i = -n;i <= n;++ i) pw[n + i] = mul (pw[n + i],upd (i));
}
}
}
int tmpS[MAXM][MAXM];
int getsum (int v){
int sum = 0;
for (Int i = 30;~i;-- i) if (v >> i & 1){
if (!val[i]) return -1;
sum ^= Siz[i],v ^= val[i];
}
return sum;
}
int tsum[21][21][MAXM];
signed main(){
freopen ("randomxor.in","r",stdin);
freopen ("randomxor.out","w",stdout);
read (n,m,L);
for (Int i = 1;i <= m;++ i) I[i][i] = 1;
fac[0] = 1;for (Int i = 1;i <= L;++ i) fac[i] = mul (fac[i - 1],i);
ifac[L] = qkpow (fac[L],mod - 2);for (Int i = L;i;-- i) ifac[i - 1] = mul (ifac[i],i);
for (Int i = 1;i <= n;++ i) read (s[i]);
for (Int i = 1;i <= m;++ i) read (t[i]);
init (),init_ntt ();
for (Int i = 1;i <= n;++ i) build_base (s[i],i);
if (siz <= 15){
for (Int i = 1;i <= n;++ i){
int pos = 0,v = s[i];
for (Int j = 30;~j;-- j) if (v >> j & 1) pos |= (1 << dfn[j] - 1),v ^= val[j];
poly h1 = poly(1,1),h2;h2.resize (2),h2[1] = 1;
for (Int k = 0;k < (1 << siz);++ k){
int s2 = __builtin_popcount (k & pos);
f[i][k] = h1 + h2 * (s2 & 1 ? (mod - 1) : 1);
}
}
for (Int i = 0;i < (1 << siz);++ i) for (Int k = 2;k <= n;++ k) f[1][i] = f[1][i] ^ f[k][i];
fwt (f[1],mod + 1 >> 1);
for (Int k1 = 0;k1 <= m;++ k1)
for (Int k2 = 1;k2 <= m;++ k2){
int nedv = t[k2] ^ t[k1],pos = 0,v = nedv;
for (Int i = 30;~i;-- i) if (v >> i & 1) pos |= (1 << dfn[i] - 1),v ^= val[i];
if (v) continue;
for (Int l = 1;l <= L;++ l){
for (Int i = 0;i < SZ(f[1][pos]) && i <= l;++ i)
Add (stp[l][k1][k2],mul (f[1][pos][i],dp[l][i]));
}
}
}
else{
for (Int i = 30;~i;-- i) if (ind[i]) vis[ind[i]] = 1;
vector <int> Spos;
for (Int i = 1;i <= n;++ i) if (!vis[i]) Spos.push_back (i);
for (Int k1 = 0;k1 <= m;++ k1) for (Int k2 = 1;k2 <= m;++ k2) tmpS[k1][k2] = getsum (t[k1] ^ t[k2]);
for (Int S = 0;S < (1 << Spos.size());++ S){
int nedv = 0,tot = 0,sum = 0,psum;
for (Int i = 0;i < Spos.size();++ i) if (S >> i & 1) nedv ^= s[Spos[i]],tot ++;psum = getsum (nedv);
for (Int k1 = 0;k1 <= m;++ k1)
for (Int k2 = 1;k2 <= m;++ k2){
if (tmpS[k1][k2] == -1) continue;
int bit = __builtin_popcount (psum ^ tmpS[k1][k2]);
tsum[k1][k2][tot + bit] ++;
}
}
for (Int l = 1;l <= L;++ l) for (Int k1 = 0;k1 <= m;++ k1) for (Int k2 = 1;k2 <= m;++ k2) for (Int ts = 0;ts <= n;++ ts) Add (stp[l][k1][k2],mul (tsum[k1][k2][ts],dp[l][ts]));
}
Poly G,T;G.resize (L),T.resize (L);
for (Int k1 = 1;k1 <= m;++ k1) G[0][k1][k1] = 1;
for (Int i = 1;i < L;++ i) for (Int k1 = 1;k1 <= m;++ k1) for (Int k2 = 1;k2 <= m;++ k2) G[i][k1][k2] = stp[i][k1][k2];
for (Int i = 1;i <= L;++ i) for (Int k = 1;k <= m;++ k) T[i - 1][k][1] = stp[i][0][k];
Poly F = inv (G,L) * T;
int ans = qkpow (n,L);
for (Int i = 0;i < L;++ i) for (Int j = 1;j <= m;++ j) Sub (ans,mul (F[i][j][1],qkpow (n,L - 1 - i)));
write (ans),putchar ('\n');
return 0;
}