胡测3 (by Chen_jr)
好长时间没写题解了,于是赶紧补一发题解。
T1 Easy Restrictions
比较套路的想法时对 \(A\) 做前缀和,得到数组 \(S\) ,由于 \(A_i\) 为正整数,因此对于任意的 \(i\) ,满足 \(S_{i+1}\ge S_i\) ,由于 \(0\le S_i\le n\) ,因此对 \(S_i\) 的取值集合 \([0,n]\) 按照 \(\operatorname{mod} c\) 分为 \(\lfloor\tfrac{n}{c}\rfloor+1\) 组,枚举 \(k\in [0,c)\) 并计算 \(g_i\) 表示所有 \(S_i=pc+k\) 的位置所产生的贡献为 \(i\) 时的方案数,容易发现答案就是所有 \(g\) 数组的卷积。
code
#include <cstdio>
using namespace std;
const int max1 = 5e3;
const int mod = 998244353;
int n, m, c, lim;
int f[max1 + 5], g[max1 + 5][2], h[max1 + 5];
void Add ( int &x, int y )
{
x = x + y;
if ( x >= mod )
x = x - mod;
return;
}
int main ()
{
freopen("limit.in", "r", stdin);
freopen("limit.out", "w", stdout);
scanf("%d%d%d", &n, &m, &c);
lim = n / c;
for ( int i = 0; i <= m; i ++ )
f[i] = 0;
f[0] = 1;
for ( int i = 0; i < c; i ++ )
{
for ( int j = 0; j <= lim; j ++ )
g[j][0] = g[j][1] = 0;
if ( !i )
{
g[0][0] = 0;
g[0][1] = 1;
}
else
{
g[0][0] = 1;
g[0][1] = 1;
}
for ( int j = 1; j <= lim; j ++ )
{
for ( int k = lim; k >= 0; k -- )
{
int g0 = g[k][0], g1 = g[k][1];
g[k][0] = g[k][1] = 0;
if ( g0 )
Add(g[k][0], g0), Add(g[k][1], g0);
if ( g1 )
Add(g[k][0], g1), Add(g[k + 1][1], g1);
}
}
if ( lim * c + i < n )
{
for ( int k = 0; k <= m; k ++ )
h[k] = f[k], f[k] = 0;
for ( int k = 0; k <= m; k ++ )
for ( int w = 0; w <= lim; w ++ )
if ( k + w <= m )
Add(f[k + w], 1LL * h[k] * ( g[w][0] + g[w][1] ) % mod);
}
else if ( lim * c + i == n )
{
for ( int k = 0; k <= m; k ++ )
h[k] = f[k], f[k] = 0;
for ( int k = 0; k <= m; k ++ )
for ( int w = 0; w <= lim; w ++ )
if ( k + w <= m )
Add(f[k + w], 1LL * h[k] * g[w][1] % mod);
}
else
{
for ( int k = 0; k <= m; k ++ )
h[k] = f[k], f[k] = 0;
for ( int k = 0; k <= m; k ++ )
for ( int w = 0; w <= lim; w ++ )
if ( k + w <= m )
Add(f[k + w], 1LL * h[k] * g[w][0] % mod);
}
}
for ( int i = 0; i <= m; i ++ )
printf("%d ", f[i]);
printf("\n");
return 0;
}
T2 Bubble Sort
设 \(pos_i\) 表示 \(i\) 在 \(a\) 序列中的位置,设 \(b_i=\sum_{k=1}^{pos_i}[a_k>i]\) ,容易发现一次冒泡排序会使 \(b_i=\max(0,b_i-1)\) 。
设最终序列分别为 \(a\) 和 \(b\) ,考虑求解 \(f(a,m)\) ,设初始序列分别为 \(a',b'\) ,容易发现满足 \(b'_i\in [0,n-i]\) 的序列 \(b'\) 与 \(a'\) 一一对应,具体构造方法为维护序列 \(C\) ,初始 \(C\) 为空,从大到小枚举 \(i\) ,将 \(i\) 插入到 \(C\) 序列中第 \(b'_i\) 个数的后面。
对于 \(b_i=0\) 的位置,可以发现 \(b'_i\in [0,m]\) ,对于 \(b_i> 0\) 的位置,需要满足 \(b'_i=b_i+m\) 。
容易发现对于 \(i\in [n-m+1,n]\) ,经过 \(m\) 次冒泡排序后一定有 \(a_i=i\) 。
因此考虑对前 \(n-m\) 个位置统计贡献,发现 \(b_i=0\) 的位置满足 \(a_{pos_i}\) 为前缀最大值,设 \(a\) 序列前 \(n-m\) 个位置中满足前缀最大值的位置数量为 \(t\) ,那么 \(f(a,m)=(m+1)^tm!\) 。
显然 \(m!\) 很容易统计,因此暂时忽略它的贡献,设 \(f_{i,j}\) 表示考虑了前 \(i\) 个数,前缀最大值为 \(j\) 时的答案,转移很简单,不再赘述。
code
#include <cstdio>
using namespace std;
const int max1 = 5e3;
const int mod = 998244353;
int n, m, lim[max1 + 5], c[max1 + 5][max1 + 5];
int f[max1 + 5], tmp[max1 + 5], sum[max1 + 5];
bool vis[max1 + 5];
int main ()
{
freopen("sort.in", "r", stdin);
freopen("sort.out", "w", stdout);
scanf("%d%d", &n, &m);
for ( int i = 1; i <= n; i ++ )
scanf("%d", &lim[i]);
for ( int i = n - m + 1; i <= n; i ++ )
if ( lim[i] && lim[i] != i )
{ printf("0\n"); return 0; }
for ( int i = n - m + 1; i <= n; i ++ )
lim[i] = i;
for ( int i = 1; i <= n; i ++ )
vis[lim[i]] = true;
for ( int k = 1; k <= n; k ++ )
c[n + 1][k] = 0;
for ( int i = n; i >= 1; i -- )
{
for ( int k = 1; k <= n; k ++ )
c[i][k] = c[i + 1][k] + ( lim[i] && lim[i] <= k );
}
f[0] = 1;
for ( int i = 1; i <= n - m; i ++ )
{
for ( int k = 0; k <= n; k ++ )
tmp[k] = sum[k] = f[k], f[k] = 0;
for ( int k = 1; k <= n; k ++ )
sum[k] = ( sum[k - 1] + sum[k] ) % mod;
if ( lim[i] )
{
for ( int k = lim[i] + 1; k <= n; k ++ )
f[k] = tmp[k];
f[lim[i]] = 1LL * sum[lim[i] - 1] * ( m + 1 ) % mod;
}
else
{
for ( int k = 1; k <= n; k ++ )
{
if ( !vis[k] )
f[k] = 1LL * sum[k - 1] * ( m + 1 ) % mod;
if ( k - i + 1 - c[i + 1][k - 1] > 0 )
f[k] = ( f[k] + 1LL * tmp[k] * ( k - i + 1 - c[i + 1][k] ) ) % mod;
}
}
}
int ans = f[n - m];
for ( int i = 1; i <= m; i ++ )
ans = 1LL * ans * i % mod;
printf("%d\n", ans);
return 0;
}
T3 Softball
对于每次询问的 \(w\) ,设 \(\operatorname{highbit}(x)\) 表示 \(x\) 在二进制下的最高位,存在一个非常优秀的构造是每次异或满足 \(\operatorname{highbit}(a_i)<\operatorname{highbit}(w)\) 的数,设满足这个条件的数的个数为 \(cnt\) ,容易发现答案一定满足 \(cnt\le ans\le cnt+1\) ,为了使得答案变成 \(cnt+1\) ,加入一个 \(\operatorname{highbit}(a_i)=\operatorname{highbit}(w)\) 的数,并且保证之后满足 \(\operatorname{highbit}(a_i)<\operatorname{highbit}(w)\) 的数均能异或,因此考虑对询问按照 \(\operatorname{highbit}\) 分组,设当前枚举的二进制位为 \(bit\) ,考虑求解所有满足 \(\operatorname{highbit}(w)=bit\) 的询问的答案,设 \(s_i\) 表示 \(i\) 位置前所有满足 \(\operatorname{highbit}(a_i)<bit\) 的数的异或和,对于一个询问,我们需要判断 \(s_{i-1}\operatorname{xor}w\ge a_i\) 并且 \(s_{j-1}\operatorname{xor}a_i\operatorname{xor}w\ge a_j\) ,其中 \(\operatorname{highbit}(a_i)=bit\) 并且 \(j>i\) 并且 \(\operatorname{highbit}(a_j)<bit\) ,首先枚举 \(i\) ,考虑优化判断的过程,如果存在 \(j<k\) 并且 \(\operatorname{highbit}(a_j)<\operatorname{highbit}(a_k)\) ,如果 \(a_k\) 的判断合法,那么操作到 \(k\) 时,\(w\) 的最高位一定大于等于 \(\operatorname{highbit}(a_k)\) ,容易发现 \(a_j\) 的判断一定合法,这样通过删去不需要判断的 \(j\) ,我们可以得到一个 \(\operatorname{highbit}\) 单调递减的序列,考虑 \(j<k\) 并且 \(\operatorname{highbit}(a_j)=\operatorname{highbit}(a_k)\) 的位置,如果 \(\operatorname{highbit}\) 相同的所有位置中的前两个满足要求,那么剩下的一定同样满足要求,因为这可以保证 \(w\) 操作到 \(k\) 时满足 \(\operatorname{highbit}(w)>\operatorname{highbit}(a_k)\) 。实际上我们只需要判断 \(O(\log{\max(a_i)})\) 个位置即可。
将询问离线并用 01 Trie 维护判断的操作,对于不合法的询问在 Trie 树上打标记,之后 Dfs 整棵树,删去合法的询问,容易发现均摊复杂度为 \(O(n\log^2(\max(a_i))+q\log(\max(a_i)))\) 。
code
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int max1 = 2e5, max2 = 1e6;
const int maxbit = 30;
int n, m, a[max1 + 5], b[max2 + 5], c[max2 + 5];
int sum[max1 + 5], pos[max1 + 5][maxbit + 5];
struct Trie
{
#define lson(now) tree[now].trans[0]
#define rson(now) tree[now].trans[1]
#define tag(now) tree[now].tag
#define del(now) tree[now].del
struct Data
{ int trans[2], tag; bool del; } tree[max2 * maxbit + 5];
int root, total;
void Clear ()
{ lson(0) = rson(0) = 0; del(0) = true; tag(0) = 0; root = total = 0; return; }
void Push_Up ( int now )
{ del(now) = del(lson(now)) && del(rson(now)); return; }
void Insert ( int &now, int x, int bit )
{
if ( !now )
{ now = ++total; lson(now) = rson(now) = tag(now) = del(now) = 0; }
if ( bit == -1 )
return;
if ( x >> bit & 1 )
Insert(rson(now), x, bit - 1);
else
Insert(lson(now), x, bit - 1);
Push_Up(now);
return;
}
void Insert ( int x )
{ return Insert(root, x, maxbit - 1); }
void Update ( int now, int bit, int x, int y, int tim )
{
if ( del(now) || tag(now) == tim || bit == -1 )
return;
if ( y >> bit & 1 )
{
tag(tree[now].trans[x >> bit & 1]) = tim;
Update(tree[now].trans[!( x >> bit & 1 )], bit - 1, x, y, tim);
}
else
{
Update(tree[now].trans[x >> bit & 1], bit - 1, x, y, tim);
}
return;
}
void Update ( int x, int y, int tim )
{ return Update(root, maxbit - 1, x, y, tim); }
void Delete ( int now, int bit, int tim )
{
if ( del(now) || tag(now) == tim )
return;
if ( bit == -1 )
{ del(now) = true; return; }
Delete(lson(now), bit - 1, tim);
Delete(rson(now), bit - 1, tim);
Push_Up(now);
return;
}
void Delete ( int tim )
{ return Delete(root, maxbit - 1, tim); }
bool Query ( int now, int bit, int x )
{
if ( del(now) )
return true;
if ( bit == -1 )
return false;
if ( x >> bit & 1 )
return Query(rson(now), bit - 1, x);
return Query(lson(now), bit - 1, x);
}
bool Query ( int x )
{ return Query(root, maxbit - 1, x); }
}Tree;
void Solve ( int bit )
{
int Max = -1;
for ( int k = 0; k < maxbit; k ++ )
pos[n + 1][k] = n + 1;
for ( int i = n; i >= 1; i -- )
{
if ( __lg(a[i]) < bit )
Max = max(Max, __lg(a[i]));
for ( int k = 0; k < maxbit; k ++ )
{
pos[i][k] = pos[i + 1][k];
if ( k - 1 == __lg(a[i]) && __lg(a[i]) == Max )
pos[i][k] = i;
}
}
for ( int i = 1; i <= n; i ++ )
for ( int k = 1; k < maxbit; k ++ )
if ( pos[i][k] == n + 1 )
pos[i][k] = pos[i][k - 1];
int ans = 0;
sum[0] = 0;
for ( int i = 1; i <= n; i ++ )
{
sum[i] = sum[i - 1];
if ( __lg(a[i]) < bit )
sum[i] ^= a[i], ++ans;
}
Tree.Clear();
for ( int i = 1; i <= m; i ++ )
if ( __lg(b[i]) == bit )
Tree.Insert(b[i]);
for ( int i = 1; i <= n; i ++ )
{
if ( __lg(a[i]) == bit )
{
Tree.Update(sum[i - 1], a[i], i);
int now = pos[i + 1][bit];
while ( now != n + 1 )
{
Tree.Update(sum[now - 1] ^ a[i], a[now], i);
int nxt = pos[now + 1][__lg(a[now]) + 1];
if ( nxt != n + 1 && __lg(a[now]) == __lg(a[nxt]) )
Tree.Update(sum[nxt - 1] ^ a[i], a[nxt], i);
now = pos[now + 1][__lg(a[now])];
}
Tree.Delete(i);
}
}
for ( int i = 1; i <= m; i ++ )
if ( __lg(b[i]) == bit )
c[i] = ans + Tree.Query(b[i]);
return;
}
int main ()
{
freopen("ball.in", "r", stdin);
freopen("ball.out", "w", stdout);
scanf("%d%d", &n, &m);
for ( int i = 1; i <= n; i ++ )
scanf("%d", &a[i]);
for ( int i = 1; i <= m; i ++ )
scanf("%d", &b[i]);
for ( int i = 0; i < maxbit; i ++ )
Solve(i);
for ( int i = 1; i <= m; i ++ )
printf("%d\n", c[i]);
return 0;
}

浙公网安备 33010602011771号