朝花夕誓
欢乐赛,祝同学们省选顺利,省选集训快乐
博客标题与内容无关,因为是退役前最后一篇博客所以使用了一个比较奇怪的标题。
T1 卷王
容易发现答案的范围很小,因此考虑枚举答案,假设当前枚举的答案为 \(i\) ,询问的状态为 \(s\) ,第 \(i\) 次操作可以给 \(s\) 异或一个长度为 \(i\) 的二进制位,判断是否可以将 \(s\) 变为 \(0\) ,考虑倒着 dp 这个过程,设 \(f_{i,s}\) 表示操作次数为 \(i\) 时, \(s\) 作为初始状态是否可以变为 \(0\) ,枚举第 \(i+1\) 次异或的长度为 \(i+1\) 的二进制位转移即可。
code
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int max1 = 16, max2 = 1 << 16;
const int inf = 0x3f3f3f3f;
bool f[max1 * 2 + 5][max2 + 5];
int ans[max1 + 5][max2 + 5];
int main ()
{
freopen("roll.in", "r", stdin);
freopen("roll.out", "w", stdout);
f[0][0] = true;
for ( int i = 1; i <= max1; i ++ )
for ( int j = 0; j < max2; j ++ )
ans[i][j] = inf;
for ( int i = 0; i <= max1 * 2; i ++ )
{
for ( int s = 0; s < max2; s ++ )
{
if ( f[i][s] )
{
for ( int k = 1; k <= max1; k ++ )
ans[k][s >> max1 - k] = min(ans[k][s >> max1 - k], i);
int siz = min(i + 1, max1);
int up = ( 1 << siz ) - 1;
up = up << max1 - siz;
for ( int k = 1; k <= max1; k ++ )
f[i + 1][s ^ ( up >> k - 1 )] = true;
f[i + 1][s] = true;
}
}
}
int T, len, qus;
char s[max1 + 5];
scanf("%d", &T);
while ( T -- )
{
scanf("%s", s + 1);
qus = 0;
len = strlen(s + 1);
for ( int i = 1; i <= len; i ++ )
if ( s[i] == '1' )
qus |= 1 << len - i;
printf("%d\n", ans[len][qus]);
}
return 0;
}
T2 赢王
发现操作为给相邻两个数加 / 减 \(1\) ,一个比较套路的想法是对序列前缀和,发现操作转化为每次可以给序列中一个数加 / 减 \(1\) ,询问将整个序列每个数变为 \(k\) 的倍数的最小操作次数(最后一个数不可以操作)。
显然一个暴力的思路是枚举每个区间并计算该区间的答案,考虑优化这个过程,考虑合法区间的贡献,设 \(s_k=\sum_{i=1}^k a_i\operatorname{mod}k\) ,容易发现一个合法的区间 \([l,r]\) 一定满足 \(s_{l-1}=s_r\) ,因此考虑枚举值 \(x\) ,找到所有满足 \(s_i\equiv x\pmod{k}\) 的位置,发现这些位置两两构成的区间一定合法,考虑相邻两点之间的区间,设区间左边有 \(L\) 个点,区间右边有 \(R\) 个点,发现这段区间会对 \(LR\) 个区间产生相同的贡献,考虑计算这个贡献,发现只需要大力分类讨论哪些值域上的数加一,哪些减一即可,这样原问题变为二维数点,可以离线做扫描线。
code
#include <cstdio>
#include <algorithm>
using namespace std;
const int max1 = 1e6;
const int mod = 998244353;
int n, k, mid, a[max1 + 5], sum[max1 + 5];
int save[max1 + 5], len, id[max1 + 5], illegal;
bool Cmp ( const int &x, const int &y )
{
if ( sum[x] == sum[y] )
return x < y;
return sum[x] < sum[y];
}
int Calc ( int x )
{ return 1LL * x * ( x - 1 ) / 2 % mod; }
struct Question
{
int opt, pos, L, R, x;
Question () {}
Question ( int __opt, int __pos, int __L, int __R, int __x )
{
opt = __opt, pos = __pos, L = __L, R = __R, x = __x;
}
bool operator < ( const Question &A ) const
{ return pos < A.pos; }
}qus[max1 * 12 + 5];
int cnt, ans;
struct Data
{
int sum1, sum2;
Data () {}
Data ( int __sum1, int __sum2 )
{ sum1 = __sum1, sum2 = __sum2; }
Data operator + ( const Data &A ) const
{ return Data(( sum1 + A.sum1 ) % mod, ( sum2 + A.sum2 ) % mod); }
Data operator - ( const Data &A ) const
{ return Data(( sum1 - A.sum1 + mod ) % mod, ( sum2 - A.sum2 + mod ) % mod); }
};
struct Bit
{
#define lowbit(now) ( now & -now )
Data tree[max1 + 5];
void Clear ()
{
for ( int i = 0; i <= len; i ++ )
tree[i] = Data(0, 0);
return;
}
void Insert ( int now, const Data &A )
{
while ( now <= len )
{
tree[now] = tree[now] + A;
now += lowbit(now);
}
return;
}
Data Query ( int now )
{
Data res = Data(0, 0);
while ( now )
{
res = res + tree[now];
now -= lowbit(now);
}
return res;
}
Data Query ( int L, int R )
{
return Query(R) - Query(L - 1);
}
}Tree;
int main ()
{
freopen("win.in", "r", stdin);
freopen("win.out", "w", stdout);
scanf("%d%d", &n, &k);
mid = k >> 1;
for ( int i = 1; i <= n; i ++ )
{
scanf("%d", &a[i]);
save[i] = sum[i] = ( sum[i - 1] + a[i] ) % k;
}
save[n + 1] = 0;
sort(save + 1, save + 2 + n);
len = unique(save + 1, save + 2 + n) - ( save + 1 );
for ( int i = 0; i <= n; i ++ )
{
sum[i] = lower_bound(save + 1, save + 1 + len, sum[i]) - save;
id[i] = i;
}
id[n + 1] = 0;
sort(id + 1, id + 2 + n, Cmp);
illegal = Calc(n + 1);
for ( int l = 1, r; l <= n + 1; l = r + 1 )
{
int kind = sum[id[l]];
r = l;
while ( r + 1 <= n + 1 && sum[id[r + 1]] == kind )
++r;
illegal = ( illegal - Calc(r - l + 1) + mod ) % mod;
kind = save[kind];
if ( kind < mid )
{
for ( int i = l; i <= r - 1; i ++ )
{
int ql = id[i] + 1, qr = id[i + 1] - 1, x = 1LL * ( i - l + 1 ) * ( r - i ) % mod;
if ( ql > qr )
continue;
qus[++cnt] = Question(1, qr, kind, kind + mid, x);
qus[++cnt] = Question(1, ql - 1, kind, kind + mid, mod - x);
qus[++cnt] = Question(2, qr, kind, kind + mid, 1LL * ( mod - x ) * kind % mod);
qus[++cnt] = Question(2, ql - 1, kind, kind + mid, 1LL * x * kind % mod);
qus[++cnt] = Question(1, qr, kind + mid + 1, k - 1, mod - x);
qus[++cnt] = Question(1, ql - 1, kind + mid + 1, k - 1, x);
qus[++cnt] = Question(2, qr, kind + mid + 1, k - 1, 1LL * x * ( k + kind ) % mod);
qus[++cnt] = Question(2, ql - 1, kind + mid + 1, k - 1, 1LL * ( mod - x ) * ( k + kind ) % mod);
qus[++cnt] = Question(1, qr, 0, kind - 1, mod - x);
qus[++cnt] = Question(1, ql - 1, 0, kind - 1, x);
qus[++cnt] = Question(2, qr, 0, kind - 1, 1LL * x * kind % mod);
qus[++cnt] = Question(2, ql - 1, 0, kind - 1, 1LL * ( mod - x ) * kind % mod);
}
}
else
{
for ( int i = l; i <= r - 1; i ++ )
{
int ql = id[i] + 1, qr = id[i + 1] - 1, x = 1LL * ( i - l + 1 ) * ( r - i ) % mod;
if ( ql > qr )
continue;
qus[++cnt] = Question(1, qr, kind - mid, kind, mod - x);
qus[++cnt] = Question(1, ql - 1, kind - mid, kind, x);
qus[++cnt] = Question(2, qr, kind - mid, kind, 1LL * x * kind % mod);
qus[++cnt] = Question(2, ql - 1, kind - mid, kind, 1LL * ( mod - x ) * kind % mod);
qus[++cnt] = Question(1, qr, kind + 1, k - 1, x);
qus[++cnt] = Question(1, ql - 1, kind + 1, k - 1, mod - x);
qus[++cnt] = Question(2, qr, kind + 1, k - 1, 1LL * ( mod - x ) * kind % mod);
qus[++cnt] = Question(2, ql - 1, kind + 1, k - 1, 1LL * x * kind % mod);
qus[++cnt] = Question(1, qr, 0, kind - mid - 1, x);
qus[++cnt] = Question(1, ql - 1, 0, kind - mid - 1, mod - x);
qus[++cnt] = Question(2, qr, 0, kind - mid - 1, 1LL * ( mod - x ) * ( kind - k + mod ) % mod);
qus[++cnt] = Question(2, ql - 1, 0, kind - mid - 1, 1LL * x * ( kind - k + mod ) % mod);
}
}
}
sort(qus + 1, qus + 1 + cnt);
Tree.Clear();
int now = 1;
for ( int i = 0; i <= n; i ++ )
{
Tree.Insert(sum[i], Data(save[sum[i]], 1));
while ( now <= cnt && qus[now].pos <= i )
{
qus[now].L = lower_bound(save + 1, save + 1 + len, qus[now].L) - save;
qus[now].R = upper_bound(save + 1, save + 1 + len, qus[now].R) - save - 1;
if ( qus[now].L <= qus[now].R )
{
if ( qus[now].opt == 1 )
ans = ( ans + 1LL * Tree.Query(qus[now].L, qus[now].R).sum1 * qus[now].x ) % mod;
else
ans = ( ans + 1LL * Tree.Query(qus[now].L, qus[now].R).sum2 * qus[now].x ) % mod;
}
++now;
}
}
ans = ( ans - illegal + mod ) % mod;
printf("%d\n", ans);
return 0;
}
T3 稳王
容易发现最优策略为手中的牌足够击杀 Boss 后直接全部弃置,考虑最终手中的牌的剩余情况,首先将期望转化为 \(1+\sum P(i)\) ,其中 \(P(i)\) 表示第 \(i\) 回合没有赢的概率。
如果最终剩余全部为复读,显然永远无法获胜,贡献为 \(\sum_{i=1}^{\infty}(\tfrac{1}{3})^i\) ;
如果只有毒药, \(n\) 回合内无法获胜,贡献为 \(\sum_{i=1}^{n}(\tfrac{1}{3})^i\) ;
如果只有火球,设 \(m=\lfloor\tfrac{n-1}{2}\rfloor\) , \(m\) 回合内无法获胜,贡献为 \(\sum_{i=1}^m(\tfrac{1}{3})^i\) ;
复读 + 火球,贡献为 \(\sum_{i=1}^m(\tfrac{2}{3})^i-2\sum_{i=1}^m(\tfrac{1}{3})^i\) ;
复读 + 毒药,此时毒药的伤害为 \(1\) ,复读的伤害为 \(2\) (第一张毒药除外),比较简单的思路为设 \(f_i\) 表示第 \(i\) 局无法击杀 Boss 的概率,转移有 \(f_i=\tfrac{1}{3}f_{i-1}+\tfrac{1}{3}f_{i-2}\) ,我们需要求解的为 \(\sum_{i=1}^n f_i\) ,可以用矩阵乘做,但是这包含了只有复读或只有火球的非法贡献,因此需要简单容斥一下。
毒药 + 火球,此时毒药的伤害为 \(1\) ,火球的伤害为 \(3\) ,与复读 + 毒药做法类似。
复读 + 毒药 + 火球,此时毒药伤害为 \(1\) ,复读伤害为 \(4\) ,火球伤害为 \(3\) ,做法与复读 + 毒药类似。
code
#include <cstdio>
using namespace std;
const int mod = 998244353;
int inv[10];
int Quick_Power ( int base, int p )
{
int res = 1;
while ( p )
{
if ( p & 1 )
res = 1LL * res * base % mod;
p >>= 1;
base = 1LL * base * base % mod;
}
return res;
}
int Calc ( int x, long long n )
{
return 1LL * ( x - Quick_Power(x, ( n + 1 ) % ( mod - 1 )) + mod ) * Quick_Power(( 1 - x + mod ) % mod, mod - 2) % mod;
}
struct Matrix
{
int matrix[5][5];
void Clear ()
{
for ( int i = 0; i < 5; i ++ )
for ( int j = 0; j < 5; j ++ )
matrix[i][j] = 0;
return;
}
void Identity ()
{
for ( int i = 0; i < 5; i ++ )
for ( int j = 0; j < 5; j ++ )
matrix[i][j] = i == j;
return;
}
Matrix operator * ( const Matrix &A ) const
{
Matrix res;
for ( int i = 0; i < 5; i ++ )
{
for ( int j = 0; j < 5; j ++ )
{
res.matrix[i][j] = 0;
for ( int k = 0; k < 5; k ++ )
res.matrix[i][j] = ( res.matrix[i][j] + 1LL * matrix[i][k] * A.matrix[k][j] ) % mod;
}
}
return res;
}
}A, B;
Matrix Quick_Power ( Matrix base, long long p )
{
Matrix res;
res.Identity();
while ( p )
{
if ( p & 1 )
res = res * base;
p >>= 1;
base = base * base;
}
return res;
}
void Solve ()
{
long long n;
int ans;
scanf("%lld", &n);
ans = ( 0LL + inv[2] + Calc(inv[3], n) + Calc(inv[3], n - 1 >> 1) + Calc(( inv[3] + inv[3] ) % mod, n - 1 >> 1) + 2LL * ( mod - Calc(inv[3], n - 1 >> 1) ) ) % mod;
// printf("ans = %d\n", ans);
// 复读+毒药
// + 复读或毒药的贡献
A.Clear(), B.Clear();
A.matrix[0][0] = inv[3], A.matrix[0][1] = inv[3], A.matrix[0][2] = 0;
A.matrix[1][0] = 1, A.matrix[1][1] = 0, A.matrix[1][2] = 0;
A.matrix[2][0] = inv[3], A.matrix[2][1] = inv[3], A.matrix[2][2] = 1;
B.matrix[0][0] = 1;
B.matrix[1][0] = 0;
B.matrix[2][0] = 0;
A = Quick_Power(A, n) * B;
ans = ( ans + A.matrix[2][0] ) % mod;
// - 全部复读的贡献
ans = ( ans - Calc(inv[3], n >> 1) + mod ) % mod;
// - 全部毒药的贡献
ans = ( ans - Calc(inv[3], n) + mod ) % mod;
// printf("ans = %d\n", ans);
// 毒药+火球
// 增加毒药或火球的贡献
A.Clear(), B.Clear();
A.matrix[0][0] = inv[3], A.matrix[0][1] = 0, A.matrix[0][2] = inv[3], A.matrix[0][3] = 0;
A.matrix[1][0] = 1, A.matrix[1][1] = 0, A.matrix[1][2] = 0, A.matrix[1][3] = 0;
A.matrix[2][0] = 0, A.matrix[2][1] = 1, A.matrix[2][2] = 0, A.matrix[2][3] = 0;
A.matrix[3][0] = inv[3], A.matrix[3][1] = 0, A.matrix[3][2] = inv[3], A.matrix[3][3] = 1;
B.matrix[0][0] = 1;
B.matrix[1][0] = 0;
B.matrix[2][0] = 0;
B.matrix[3][0] = 0;
A = Quick_Power(A, n) * B;
ans = ( ans + A.matrix[3][0] ) % mod;
// - 全部毒药的贡献
ans = ( ans - Calc(inv[3], n) + mod ) % mod;
// - 全部火球的贡献
ans = ( ans - Calc(inv[3], n / 3) + mod ) % mod;
// printf("ans = %d\n", ans);
// 复读+毒药+火球
// 增加复读或毒药或火球的贡献
A.Clear(), B.Clear();
A.matrix[0][0] = inv[3], A.matrix[0][1] = 0, A.matrix[0][2] = inv[3], A.matrix[0][3] = inv[3], A.matrix[0][4] = 0;
A.matrix[1][0] = 1, A.matrix[1][1] = 0, A.matrix[1][2] = 0, A.matrix[1][3] = 0, A.matrix[1][4] = 0;
A.matrix[2][0] = 0, A.matrix[2][1] = 1, A.matrix[2][2] = 0, A.matrix[2][3] = 0, A.matrix[2][4] = 0;
A.matrix[3][0] = 0, A.matrix[3][1] = 0, A.matrix[3][2] = 1, A.matrix[3][3] = 0, A.matrix[3][4] = 0;
A.matrix[4][0] = inv[3], A.matrix[4][1] = 0, A.matrix[4][2] = inv[3], A.matrix[4][3] = inv[3], A.matrix[4][4] = 1;
B.matrix[0][0] = 1;
B.matrix[1][0] = 0;
B.matrix[2][0] = 0;
B.matrix[3][0] = 0;
B.matrix[4][0] = 0;
A = Quick_Power(A, n) * B;
ans = ( ans + A.matrix[4][0] ) % mod;
// printf("ans = %d\n", ans);
// 减去复读或毒药的贡献
A.Clear(), B.Clear();
A.matrix[0][0] = inv[3], A.matrix[0][1] = 0, A.matrix[0][2] = 0, A.matrix[0][3] = inv[3], A.matrix[0][4] = 0;
A.matrix[1][0] = 1, A.matrix[1][1] = 0, A.matrix[1][2] = 0, A.matrix[1][3] = 0, A.matrix[1][4] = 0;
A.matrix[2][0] = 0, A.matrix[2][1] = 1, A.matrix[2][2] = 0, A.matrix[2][3] = 0, A.matrix[2][4] = 0;
A.matrix[3][0] = 0, A.matrix[3][1] = 0, A.matrix[3][2] = 1, A.matrix[3][3] = 0, A.matrix[3][4] = 0;
A.matrix[4][0] = inv[3], A.matrix[4][1] = 0, A.matrix[4][2] = 0, A.matrix[4][3] = inv[3], A.matrix[4][4] = 1;
B.matrix[0][0] = 1;
B.matrix[1][0] = 0;
B.matrix[2][0] = 0;
B.matrix[3][0] = 0;
B.matrix[4][0] = 0;
A = Quick_Power(A, n) * B;
ans = ( ans - A.matrix[4][0] + mod ) % mod;
// printf("ans = %d\n", ans);
// 减去复读或火球的贡献
A.Clear(), B.Clear();
A.matrix[0][0] = 0, A.matrix[0][1] = 0, A.matrix[0][2] = inv[3], A.matrix[0][3] = inv[3], A.matrix[0][4] = 0;
A.matrix[1][0] = 1, A.matrix[1][1] = 0, A.matrix[1][2] = 0, A.matrix[1][3] = 0, A.matrix[1][4] = 0;
A.matrix[2][0] = 0, A.matrix[2][1] = 1, A.matrix[2][2] = 0, A.matrix[2][3] = 0, A.matrix[2][4] = 0;
A.matrix[3][0] = 0, A.matrix[3][1] = 0, A.matrix[3][2] = 1, A.matrix[3][3] = 0, A.matrix[3][4] = 0;
A.matrix[4][0] = 0, A.matrix[4][1] = 0, A.matrix[4][2] = inv[3], A.matrix[4][3] = inv[3], A.matrix[4][4] = 1;
B.matrix[0][0] = 1;
B.matrix[1][0] = 0;
B.matrix[2][0] = 0;
B.matrix[3][0] = 0;
B.matrix[4][0] = 0;
A = Quick_Power(A, n) * B;
ans = ( ans - A.matrix[4][0] + mod ) % mod;
// printf("ans = %d\n", ans);
// 减去毒药或火球的贡献
A.Clear(), B.Clear();
A.matrix[0][0] = inv[3], A.matrix[0][1] = 0, A.matrix[0][2] = inv[3], A.matrix[0][3] = 0, A.matrix[0][4] = 0;
A.matrix[1][0] = 1, A.matrix[1][1] = 0, A.matrix[1][2] = 0, A.matrix[1][3] = 0, A.matrix[1][4] = 0;
A.matrix[2][0] = 0, A.matrix[2][1] = 1, A.matrix[2][2] = 0, A.matrix[2][3] = 0, A.matrix[2][4] = 0;
A.matrix[3][0] = 0, A.matrix[3][1] = 0, A.matrix[3][2] = 1, A.matrix[3][3] = 0, A.matrix[3][4] = 0;
A.matrix[4][0] = inv[3], A.matrix[4][1] = 0, A.matrix[4][2] = inv[3], A.matrix[4][3] = 0, A.matrix[4][4] = 1;
B.matrix[0][0] = 1;
B.matrix[1][0] = 0;
B.matrix[2][0] = 0;
B.matrix[3][0] = 0;
B.matrix[4][0] = 0;
A = Quick_Power(A, n) * B;
ans = ( ans - A.matrix[4][0] + mod ) % mod;
// printf("ans = %d\n", ans);
// 减去全部为复读,毒药,火球的贡献
ans = ( ans + Calc(inv[3], n / 4) ) % mod;
ans = ( ans + Calc(inv[3], n / 3) ) % mod;
ans = ( ans + Calc(inv[3], n) ) % mod;
ans = ( ans + 1 ) % mod;
printf("%d\n", ans);
return;
}
int main ()
{
freopen("stable.in", "r", stdin);
freopen("stable.out", "w", stdout);
inv[1] = 1;
for ( int i = 2; i < 10; i ++ )
inv[i] = 1LL * ( mod - mod / i ) * inv[mod % i] % mod;
int T;
scanf("%d", &T);
while ( T -- )
Solve();
return 0;
}

浙公网安备 33010602011771号