2021山东省赛


这场7题没啥难度,但之后是真的难开,补一道卡一道,本来是要补10题的,考虑难度和效果就改9题了。

A. Beta Go

待补,详见ZAwei的博客

B. Build Roads

随机化。$n$较大时答案大概率为$n-1$,较小时暴力,注意$L=R$的情况。

C. Cat Virus

构造。考虑对题目增加限制:要求构造出来的必须是二叉树。当某点左子树答案为$A$,右子树答案为$B$,则该点子树的答案为$A\times B+1$,只有一个孩子的结点答案是孩子的答案$+1$,所以可以递归构造出$k$。

D. Dyson Box

签到。考虑重力向下,第$i$列高为$h_i$,答案就是

$有方块的位置数\times2+\sum_{i=1}^{n+1}|h_i-h_{i-1}|(h_{i+1}=0)$。

E. Evaluate Expression

F. Birthday Cake

字符串。hash老题了,考虑两个字符$a,b\ (len(a)\le len(b))$串拼接后合法的情况:

  • $a,b$相同
  • $b$的前$\frac{len(a)+len(b)}{2}$个字符是拼接后串的一半

$map$记录一下就行,小心被卡模数和自然溢出。

G. Grade Point Average

签到。

H. Adventurer’s Guild

签到。

I. Chemical Code

线段树。考虑当加入一个元素、数字、括号时产生的影响:

元素:相当于单点加,如果后继是数字则要:

  1. 减去数字的影响(区间除)
  2. 单点修改,加某个值
  3. 加上数字的影响(区间乘)

数字:相当于区间乘,如果前驱是括号则:

  1. 找到配对的另一个括号
  2. 将括号扩起的区间乘某个值

括号:如果后继是数字,处理方法和元素相同

但是,由于模数不是质数,这个区间除就很麻烦了(需要CRT)。但是考虑数字只有$1-9$,因此可以将复杂度$\times10$,懒标记永久化维护$1-9$在线段树上每个节点乘的次数,由于每次都是查询1到n,所以不用pushdown,常数还巨小。

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+7;
const int MOD=985151223;
int n,q;
ll p[10][N];
map<char,int> mp={{'C',12},{'H',1},{'O',16}};
struct Tag{
int mul[10];
Tag(){
memset(mul,0,sizeof mul);
}
ll cal(ll w){
for(int i=2;i<=9;i++){
w=w*p[i][mul[i]]%MOD;
}
return w;
}
};
struct SegmentTree{
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
ll a[N<<2];
Tag b[N<<2];
void pushup(int o){
a[o]=b[o<<1].cal(a[o<<1])+b[o<<1|1].cal(a[o<<1|1]);
a[o]%=MOD;
}
void build(int o,int l,int r){
if(l==r){
a[o]=0;
return;
}
int mid=l+r>>1;
build(lson);
build(rson);
pushup(o);
}
void add(int o,int l,int r,int P,int X){
if(l==r){
a[o]+=X;
return;
}
int mid=l+r>>1;
if(P<=mid)add(lson,P,X);
else add(rson,P,X);
pushup(o);
}
void multi(int o,int l,int r,int L,int R,int X,int Y){
if(L<=l&&r<=R){
b[o].mul[X]+=Y;
return;
}
int mid=l+r>>1;
if(L<=mid)multi(lson,L,R,X,Y);
if(R>mid)multi(rson,L,R,X,Y);
pushup(o);
}
}T;
int vis[N],l[N],r[N];
set<int> se;
int getpre(int p){
auto k=se.lower_bound(p);
if(k==se.begin())return -1;
return *--k;
}
int getnxt(int p){
auto k=se.upper_bound(p);
if(k==se.end())return -1;
return *k;
}
void op1(char ch,int x){
int w=mp[ch];
if(isdigit(ch)){
vis[x]=w;
int pre=getpre(x);
assert(pre!=-1);
T.multi(1,1,n,l[pre],pre,w,1);
}else{
int nxt=getnxt(x);
int pre=getpre(x);
if(nxt!=-1&&vis[nxt]>0){
if(pre!=-1){
T.multi(1,1,n,l[pre],pre,vis[nxt],-1);
}
T.multi(1,1,n,x,x,vis[nxt],1);
}
T.add(1,1,n,x,w);
}
l[x]=r[x]=x;
se.insert(x);
}
void op2(int x,int y){
l[y]=x;
r[x]=y;
int nxt=getnxt(y);
int pre=getpre(y);
se.insert(x);
se.insert(y);
vis[x]=vis[y]=-1;
if(nxt!=-1&&vis[nxt]>0){
if(pre!=-1){
T.multi(1,1,n,l[pre],pre,vis[nxt],-1);
}
T.multi(1,1,n,x,y,vis[nxt],1);
}
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>q;
for(int i=1;i<=9;i++){
mp[i+'0']=i;
p[i][0]=1;
for(int j=1;j<N;j++){
p[i][j]=p[i][j-1]*i%MOD;
}
}
while(q--){
int op,x,l,r;
char ch;
cin>>op;
if(op==1){
cin>>ch>>x;
op1(ch,x);
}else{
cin>>l>>r;
op2(l,r);
}
cout<<T.b[1].cal(T.a[1])<<"\n";
}
}

J. Tuition Agent

K. Piggy Calculator

感觉很厉害,有时间就补。

L. Construction of 5G Base Station

概率dp。考虑用$f_{i,j}$表示$i$连到$j$位置的基站的概率,则有

$f_{i,j}=\frac{p_j}{1-\prod^{n}_{k=1}{(1-p_k)}}\prod_{i到k比j优先}(1-p_k)$

直观来理解,$p_j$下面那一串是$i$连遍所有基站都失败的概率,后面那一块就是$i$和优先于$j$的所有基站都匹配失败的概率。

【前置芝士1:一次试验为真的概率为$p$,如果失败会重复直到为真结束,则其期望结束次数为$\frac{1}{p}$】

(考虑等比数列求和 或 解方程:$E(x)=p\times0+(1-p)\times E(x)+1$即可证明)

设$E(x_i)$为匹配到基站$i$的人个数的期望,则有

$E(x_i)=\frac{p_i}{1-\prod^{n}_{k=1}{(1-p_k)}}\sum_{j=1}^{n}\prod_{j到k比i优先}(1-p_k)$

然后题解说,容易发现这东西后一块能用前缀和递推,(赛中感觉不可以递推就没搞,下次遇到这种最好打打草稿看看规律)

一顿操作能$O(n)$求出$i\in[1,n]$的$E(x_i)$,

(PS:这里$Hile$改了2h才过)

但是题目要求的是$E(\sum_{i=1}^{n}{x_i}^2)$,这时就要利用期望的线性性拆开了。

【前置芝士2:如果变量$X,Y$相互独立,则$E(X+Y)=E(X)+E(Y), E(XY)=E(X)E(Y)$】

$E(\sum_{i=1}^{n}x_i^2)=\sum_{i=1}^{n}E(x_i^2)$,

此时,对于每一个$E(x_i^2)$,$x_i$可以看作$n$个变量$y$之和,其中$y_i=1$表示第$i$个人连接了该基站,所以有

$E(x)=\sum_{i=1}^{n}E(y_i)$

原式可写为

$E((\sum_{i=1}^{n}y_i)^2)=(E(x))^2-\sum_{i=1}^{n}(E(y_i))^2+E(x)$

其中$\sum_{i=1}^n(E(y_i))^2$可以在求$E(x)$的过程中一起计算。

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+7;
const int MOD=998244353;
int n,m,p[N],q[N],f[N],g[N],s[N],t[N],A[2][N],B[2][N];
ll S=1,ans;
ll qpow(ll a,ll n=MOD-2,ll m=MOD){
ll res=1;
a%=m;
while(n){
if(n&1)res=res*a%m;
a=a*a%m,n>>=1;
}
return res;
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1,x,y;i<=m;i++){
cin>>x>>y;
p[x]=y;
}
for(int i=0;i<=n+1;i++){
q[i]=(1-p[i]+MOD)%MOD;
s[i]=i==0?1:1ll*s[i-1]*q[i]%MOD;
S=S*q[i]%MOD;
}
for(int i=n+1;i>=0;i--){
t[i]=i==n+1?1:1ll*t[i+1]*q[i]%MOD;
}
for(int i=1;i<=n;i++){
A[0][i]=i==1?0:(i==2?q[1]:(1ll*A[0][i-2]*q[i-1]%MOD*q[i-2]%MOD+s[i-1]+1ll*q[i-1]*q[i-2]%MOD)%MOD);
A[1][i]=i==1?0:(i==2?1ll*q[1]*q[1]%MOD:(1ll*A[1][i-2]*q[i-1]%MOD*q[i-2]%MOD*q[i-1]%MOD*q[i-2]%MOD+1ll*s[i-1]*s[i-1]+1ll*q[i-1]*q[i-2]%MOD*q[i-1]%MOD*q[i-2]%MOD)%MOD);
f[i]=(f[i]+A[0][i])%MOD;
g[i]=(g[i]+A[1][i])%MOD;
}
for(int i=n;i>=1;i--){
B[0][i]=i==n?0:(i==n-1?q[n]:(1ll*B[0][i+2]*q[i+1]%MOD*q[i+2]%MOD+t[i+1]+q[i+1])%MOD);
B[1][i]=i==n?0:(i==n-1?1ll*q[n]*q[n]%MOD:(1ll*B[1][i+2]*q[i+1]%MOD*q[i+2]%MOD*q[i+1]%MOD*q[i+2]%MOD+1ll*t[i+1]*t[i+1]%MOD+1ll*q[i+1]*q[i+1]%MOD)%MOD);
f[i]=(f[i]+B[0][i])%MOD;
g[i]=(g[i]+B[1][i])%MOD;
}
for(int i=1;i<=n;i++){
f[i]++,g[i]++;
}
S=qpow(1-S+MOD);
for(int i=1;i<=n;i++){
f[i]=1ll*f[i]*p[i]%MOD*S%MOD;
g[i]=1ll*g[i]*p[i]%MOD*p[i]%MOD*S%MOD*S%MOD;
f[i]=(1ll*f[i]*f[i]%MOD-g[i]+MOD+f[i])%MOD;
ans=(ans+f[i])%MOD;
}
cout<<ans;
}

M. Matrix Problem

签到。