A层省选5
A. 卷王
赛时打表,把电脑搞炸两次
打表打了一个小时你敢信
这题先进行题意转换,发现如果\(t\)秒结束,相当于我们可以选择长度为\(t\), \(t - 1\) ......\(1\)的子串各一个进行反转
那么就可以进行\(DP\)了,设\(dp_{i,j}\)表示后\(i\)秒,能否到达状态\(j\),转移考虑反转哪个区间
code
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
ll x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
const int maxn = 100005;
ll n, l, r;
ll work(){
if(l == r)return 1;
int hi = 0; for(int i = 63; i >= 0; --i)if(((1ll << i) & l) != ((1ll << i) & r)){hi = i; break;}
int low = -1; for(int i = hi - 1; i >= 0; --i)if(r & (1ll << i)){low = i; break;}
ll nans = (1ll << hi) - (l & ((1ll << hi) - 1)), rl = (1ll << hi) - nans;
ll lr = (1ll << (low + 1)) - 1;
nans = nans << 1;
return nans + min(lr, rl - 1) + 1;
}
int main(){
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
int T = read();
for(register int ask = 1; ask <= T; ++ask){
n = read(), l = read(), r = read();
if(n > 1)printf("%lld\n",work());
else printf("%lld\n", r - l + 1);
}
return 0;
}
B. 赢王
首先考虑暴力处理\(l - r\)的最小操作次数,那么对于最左边的只能由右侧处理,我们操作次数就是\(min(x, k - x)\),然后下一个就变成最左侧了
所以\(n^3\)暴力就是这样
我们发现区间\(l ,r\)合法,当且仅当\(k | (sum_r - sum_{l - 1})\)结合上面的暴力可以发现上面的\(x\)就是\(sum_i - sum_{l - 1}\)
而且可以发现区间\(a,b\)合法操作次数为\(d\),区间\(b + 1, c\)合法操作次数为\(e\)那么区间\(a, c\)合法,操作次数为\(d + e\)
所以可以按照\(sum_i \% k\)对\(i\)分类,统计一个区间被计算了多少次,大大优化了暴力
现在我们发现限制我们的主要是求教操作次数的部分,考虑使用主席树维护一下
具体的,对每个前缀维护\(sum == i\)的有多少,以及子树和,求解时根据不同值与\(sum_{l - 1}\)的大小关系分情况讨论即可
code
#include<map>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<iostream>
#include<set>
using namespace std;
typedef pair<int, int> pii;
const int maxn = 1000005;
const int mod = 998244353;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
int root[maxn],n, k, a[maxn], sum[maxn], nxt[maxn], head[maxn], tot;
map<int, int>mp;
long long ans, del;
struct tree{
#define ls t[x].l
#define rs t[x].r
struct node{int l, r, size, sum;}t[maxn * 28];
int cnt;
void push_up(int x){
t[x].size = t[ls].size + t[rs].size;
t[x].sum = (t[ls].sum + t[rs].sum) % mod;
}
void modify(int &x, int rt, int l, int r, int pos){
if(!x) x = ++cnt, t[x] = t[rt];
if(l == r){
++t[x].size;
t[x].sum += l;t[x].sum %= mod;
return;
}
int mid = (l + r) >> 1;
if(pos <= mid)t[x].l = 0, modify(t[x].l, t[rt].l, l, mid, pos);
else t[x].r = 0, modify(t[x].r, t[rt].r, mid + 1, r, pos);
push_up(x);
}
pii query(int x, int rt, int l, int r, int L, int R){
if(!x || t[x].size - t[rt].size <= 0 || L > R)return pii(0, 0);
if(L <= l && r <= R)return pii(t[x].sum - t[rt].sum, t[x].size - t[rt].size);
int mid = (l + r) >> 1;
pii ans = pii(0, 0);
if(L <= mid){
pii y = query(t[x].l, t[rt].l, l, mid, L, R);
ans.first += y.first;
ans.first %= mod;
ans.second += y.second;
}
if(R > mid){
pii y = query(t[x].r, t[rt].r, mid + 1, r, L, R);
ans.first += y.first;
ans.first %= mod;
ans.second += y.second;
}
return ans;
}
}t;
int work(int l, int r){
long long nans = 0;
int s = sum[l - 1], mk = k >> 1;
if(s <= mk){
pii x = t.query(root[r], root[l - 1], 0, k - 1, s + 1, s + mk);
x.first = (x.first + mod) % mod;
pii y = t.query(root[r], root[l - 1], 0, k - 1, 0, s - 1);
y.first = (y.first + mod) % mod;
pii z = t.query(root[r], root[l - 1], 0, k - 1, s + mk + 1, k - 1);
z.first = (z.first + mod) % mod;
nans += x.first - x.second * 1ll * s;
nans += s * 1ll * y.second - y.first;
nans += k * 1ll * z.second - (z.first - s * 1ll * z.second);
}else{
int ll = (s + mk) % k;
pii x = t.query(root[r], root[l - 1], 0, k - 1, 0, ll);
x.first = (x.first + mod) % mod;
pii y = t.query(root[r], root[l - 1], 0, k - 1, ll + 1, s - 1);
y.first = (y.first + mod) % mod;
pii z = t.query(root[r], root[l - 1], 0, k - 1, s + 1, k - 1);
z.first = (z.first + mod) % mod;
nans += x.second * 1ll * (k - s) + x.first;
nans += 1ll * y.second * s - y.first;
nans += z.first - s * 1ll * z.second;
}
return nans % mod;
}
int main(){
freopen("win.in","r",stdin);
freopen("win.out","w",stdout);
n = read(), k = read();
for(int i = 1; i <= n; ++i)a[i] = read();
for(int i = 1; i <= n; ++i)sum[i] = (sum[i - 1] + a[i]) % k;
t.modify(root[0], 0, 0, k - 1, 0);
for(int i = 1; i <= n; ++i) t.modify(root[i], root[i - 1], 0, k - 1, sum[i]);
mp[0] = 1; head[++tot] = 0;
for(int i = 1; i <= n; ++i){
if(mp[sum[i]])nxt[mp[sum[i]] - 1] = i;
else head[++tot] = i;
mp[sum[i]] = i + 1;
}
del = 1ll * n * (n + 1) / 2;
for(int i = 1; i <= tot; ++i){
int s = 0, now = head[i];
do{
++s;
now = nxt[now];
}while(now);
del -= (1ll * s * (s - 1) / 2);
int pl = 0; now = head[i];
while(nxt[now]){
++pl;
ans += 1ll * pl * (s - pl) % mod * work(now + 1, nxt[now]) % mod;
now = nxt[now];
}
}
ans = ((ans - del) % mod + mod) % mod;
printf("%lld\n",ans);
return 0;
}
C. 稳王
题解讲的很详细了
最优策略是能秒掉\(boss\)再出牌
求杀死\(boss\)的期望就是求杀不死\(boss\)的期望 + 1
然后分情况讨论
只有毒药/复读/火球,有两个,有三个的情况
具体题解讲的很详细了,,,,(主要是懒的打了)
那个合式证明设\(s = \sum\),用\(xs - s\)就行
注意一点就是矩阵的部分先认为第一张也有伤害,最后减去不合法的方案即可
代码还有一点小注释
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<iostream>
#include<set>
using namespace std;
typedef long long ll;
const int mod = 998244353;
const int inv2 = 499122177, inv3 = 332748118;
const int maxn = 100;
ll qpow(ll x, ll y){
y %= (mod - 1);
ll ans = 1;
for(; y; y >>= 1, x = x * x % mod)if(y & 1)ans = ans * x % mod;
return ans;
}
struct matrix{
ll a[5][5]; int n;
matrix(){memset(a, 0, sizeof(a));}
void clear(){memset(a, 0, sizeof(a));}
matrix operator * (matrix x) const{
matrix ans; ans.n = n;
for(int i = 0; i < n; ++i)
for(int k = 0; k < n; ++k)
for(int j = 0; j < n; ++j)
ans.a[i][j] = (ans.a[i][j] + a[i][k] * x.a[k][j]) % mod;
return ans;
}
};
matrix qpow(matrix x, ll y){
matrix ans; ans.n = x.n;
for(int i = 0; i < ans.n; ++i)ans.a[i][i] = 1;
for(; y; y >>= 1, x = x * x)if(y & 1)ans = ans * x;
return ans;
}
ll clac(ll x, ll n){
// \sum_{i = 1}^{n} x^i = x * (x ^ n - 1) / (x - 1)
//这里是求 \sum_{i = 1}^n inv_x^i
return 1ll * x * (1 - qpow(x , n)) % mod * qpow(1 - x + mod, mod - 2) % mod;
}
int main(){
freopen("stable.in","r",stdin);
freopen("stable.out","w",stdout);
int t; scanf("%d",&t);
for(int i = 1; i <= t; ++i){
ll n; scanf("%lld",&n);
ll m = (n - 1) / 2, ans = 0;
//只有复读
ans += inv2;
//只有毒药
ans += inv2 * ((1 - qpow(inv3, n) + mod) % mod) % mod;
//只有火球
ans += inv2 * ((1 - qpow(inv3, m) + mod) % mod) % mod;
//火球 + 复读
ans += 2 * (1 - qpow(inv3 * 2, m) + mod) % mod - (1 - qpow(inv3, m));
ans = (ans % mod + mod) % mod;
// cerr << ans << endl;
//注意我们实际上统计杀不死的情况的贡献,下面矩阵都加上了前n项,其实是前n-1项(因为有一张毒药没有伤害)
//复读 + 毒药
/*
先默认都有伤害,再去掉不合法的
[s , s - 1, sum]
inv3, 1, inv3
inv3, 0, inv3
0, 0, 1
sum 为 s 项的和,(2,2) 之前的和, 其他系数与S 一致,相当于加S
*/
matrix a, b;
a.n = b.n = 3;
a.a[0][0] = 1;
b.a[0][0] = b.a[1][0] = b.a[0][2] = b.a[1][2] = inv3;
b.a[0][1] = b.a[2][2] = 1;
a = a * qpow(b, n);
ans = (ans + a.a[0][2]) % mod;
ans = (ans - clac(inv3, n / 2) - clac(inv3, n)) % mod;
//减去全是复读(默认伤害2,实际没有伤害),全是毒药(不在这里统计)
ans = (ans % mod + mod) % mod;
a.clear(), b.clear();
// cerr << ans << endl;
// 毒药 + 火球
//类似上面
a.n = b.n = 4;
a.a[0][0] = 1;
b.a[0][0] = b.a[0][3] = b.a[2][0] = b.a[2][3] = inv3;
b.a[0][1] = b.a[1][2] = b.a[3][3] = 1;
a = a * qpow(b, n);
ans = (ans + a.a[0][3]) % mod;
ans = (ans - clac(inv3, n) - clac(inv3, n / 3)) % mod;
ans = (ans % mod + mod) % mod;
a.clear(), b.clear();
// cerr << ans << endl;
//毒药 + 火球 + 复读
//需要容斥一下
//step1 : + 都有 伤害; 1 3 4
a.n = b.n = 5;
a.a[0][0] = 1;
b.a[0][0] = b.a[0][4] = b.a[2][0] = b.a[2][4] = b.a[3][0] = b.a[3][4] = inv3;
b.a[0][1] = b.a[1][2] = b.a[2][3] = b.a[4][4] = 1;
a = a * qpow(b, n);
ans = (ans + a.a[0][4]) % mod;
a.clear(), b.clear();
// cerr << ans << endl;
// step2 : - 毒药 + 火球
a.a[0][0] = 1;
b.a[0][0] = b.a[0][4] = b.a[2][0] = b.a[2][4] = inv3;
b.a[0][1] = b.a[1][2] = b.a[2][3] = b.a[4][4] = 1;
a = a * qpow(b, n);
ans = (ans - a.a[0][4] + mod) % mod;
a.clear(), b.clear();
// cerr << ans << endl;
// step3 : - 毒药 + 复读
a.a[0][0] = 1;
b.a[0][0] = b.a[0][4] = b.a[3][0] = b.a[3][4] = inv3;
b.a[0][1] = b.a[1][2] = b.a[2][3] = b.a[4][4] = 1;
a = a * qpow(b, n);
ans = (ans - a.a[0][4] + mod) % mod;
a.clear(), b.clear();
// cerr << ans << endl;
// step 4 : - 火球 + 复读
a.a[0][0] = 1;
b.a[2][0] = b.a[2][4] = b.a[3][0] = b.a[3][4] = inv3;
b.a[0][1] = b.a[1][2] = b.a[2][3] = b.a[4][4] = 1;
a = a * qpow(b, n);
ans = (ans - a.a[0][4] + mod) % mod;
a.clear(), b.clear();
// cerr << ans << endl;
// step 5 + 全是火球/复读/毒药
ans = (ans + clac(inv3, n) + clac(inv3, n / 3) + clac(inv3, n / 4)) % mod;
//记得 + 1
printf("%lld\n",(ans + 1 + mod) % mod);
}
return 0;
}