【比赛记录】2025CSP-S模拟赛26
| A | B | C | D | Sum | Rank |
|---|---|---|---|---|---|
| 60 | - | - | 20 | 80 | 16/24 |
A. 集合
首先发现固定左端点 \(l\),好的子区间的右端点是从 \(l\) 开始的一段连续的位置。这是因为一个好的区间,其子区间必然也是好的。于是双指针,用权值线段树维护连续段即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=2e5+5;
int n,m,a[maxn],cnt[maxn];
struct seg{
int len,sum,ls,rs,lc,rc;
seg(int len=0,int sum=0,int ls=0,int rs=0,int lc=0,int rc=0)
:len(len),sum(sum),ls(ls),rs(rs),lc(lc),rc(rc){}
il seg operator+(const seg &x)const{
seg res;
res.lc=lc,res.rc=x.rc;
res.ls=ls,res.rs=x.rs;
res.len=len+x.len;
res.sum=max(sum,x.sum);
if(rc+1==x.lc){
res.sum=max(res.sum,rs+x.ls);
if(ls==len){
res.ls=len+x.ls;
}
if(x.rs==x.len){
res.rs=x.len+rs;
}
}
return res;
}
}tr[maxn<<2];
il void pushup(int id){
tr[id]=tr[lid]+tr[rid];
}
il void upd(int id,int l,int r,int p,seg v){
if(l==r){
tr[id]=v;
return ;
}
int mid=(l+r)>>1;
if(p<=mid){
upd(lid,l,mid,p,v);
}
else{
upd(rid,mid+1,r,p,v);
}
pushup(id);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
ll ans=0;
for(int l=1,r=1;r<=n;r++){
if(cnt[a[r]]++==0){
upd(1,1,n,a[r],seg(1,1,1,1,a[r],a[r]));
}
while(tr[1].sum>m){
if(--cnt[a[l]]==0){
upd(1,1,n,a[l],seg());
}
l++;
}
ans+=r-l+1;
}
cout<<ans;
return 0;
}
}
int main(){return asbt::main();}
B. 差后队列
首先考虑 pop 操作,从前往后扫,维护当前时刻除了最大值之外的和的期望,设当前有 \(cnt\) 个数,则 \(ans_i=\frac{sum}{cnt}\)。
然后考虑 push 操作,倒过来扫,每个数在它可以被删去的位置计算答案。因为除了最大值之外的其它数都是等价的,所以直接维护答案即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1e6+5,mod=998244353;
int n,inv[maxn],ans[maxn],L[maxn],R[maxn],tot,hp[maxn],b[maxn];
struct node{
int opt,x;
}a[maxn];
il void solve(int l,int r){
for(int i=l,cnt=0,sum=0,mxx=0;i<=r;i++){
if(a[i].opt){
if(cnt--==1){
ans[i]=mxx;
}
else{
ans[i]=sum*1ll*inv[cnt]%mod;
sum=sum*1ll*(1+mod-inv[cnt])%mod;
}
}
else{
cnt++;
if(a[i].x<mxx){
(sum+=a[i].x)%=mod;
}
else{
(sum+=mxx)%=mod,mxx=a[i].x;
}
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
inv[1]=1;
for(int i=2;i<=n;i++){
inv[i]=(mod-mod/i)*1ll*inv[mod%i]%mod;
}
for(int i=1,cnt=0,j=1,lst=0;i<=n;i++){
cin>>a[i].opt;
if(a[i].opt){
if(--cnt==0){
L[++tot]=j,R[tot]=i,j=i+1;
ans[lst]=i,lst=0;
}
}
else{
cin>>a[i].x;
cnt++;
if(!lst){
lst=i;
}
else if(a[i].x>a[lst].x){
b[i]=lst,lst=i;
}
else{
b[i]=i;
}
}
hp[i]=cnt;
}
// for(int i=1;i<=n;i++){
// cout<<hp[i]<<" ";
// }
// cout<<"\n";
if(R[tot]!=n){
tot++;
L[tot]=R[tot-1]+1,R[tot]=n;
}
for(int i=1;i<=tot;i++){
solve(L[i],R[i]);
}
for(int i=n,res=0;i;i--){
if(a[i].opt){
if(hp[i]){
res=(res*1ll*(1+mod-inv[hp[i]])+i*1ll*inv[hp[i]])%mod;
}
}
else{
ans[b[i]]=res;
}
}
for(int i=1;i<=n;i++){
cout<<ans[i]<<" ";
}
return 0;
}
}
int main(){return asbt::main();}
C. 蛋糕
设 \(dp_{l,r,p}\) 表示区间 \([l,r]\),只考虑第 \(p\) 层以上的最小权值。记 \(f_{l,r}\) 表示 \([l,r]\) 最小值的下标,\(g_{l,r}\) 表示最大值。
考虑转移,可以证明只有以下两种转移:
-
\(dp_{l,r,p}\leftarrow dp_{l,f_{l,r}-1,a_{f_{l,r}}}+dp_{f_{l,r}+1,r,a_{f_{l,r}}}+\frac{(2\times a_{g_{l,r}}-a_{f_{l,r}}+1-p)\times(a_{f_{l,r}}-p)}{2}\)
-
\(dp_{l,r,p}\leftarrow dp_{l,g_{l,r}-1,p}+dp_{g_{l,r}+1,r,p}+a_{g_{l,r}}-p\)
然后还可以证明,有效状态只有 \(n^2\) 个。哈希表 + 记搜即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=3e3+5,HM=1e7+19;
const ll inf=1e18;
int n,f[maxn][maxn],g[maxn][maxn],cnt,hd[HM+5];
ll a[maxn];
struct{
int nxt;
ll key,val;
}e[4500005];
il void encode(ll key,ll val){
int t=key%HM;
e[++cnt].nxt=hd[t];
e[cnt].key=key,e[cnt].val=val;
hd[t]=cnt;
}
il ll decode(ll key){
for(int i=hd[key%HM];i;i=e[i].nxt){
if(e[i].key==key){
return e[i].val;
}
}
return -1;
}
il ll hash(int l,int r,ll p){
return ((l*4000ll+r)<<32)+p;
}
il ll dfs(int l,int r,ll p){
if(l>r){
return 0;
}
ll tmp=decode(hash(l,r,p));
if(~tmp){
return tmp;
}
ll res=min(dfs(l,f[l][r]-1,a[f[l][r]])+dfs(f[l][r]+1,r,a[f[l][r]])+(2*a[g[l][r]]-a[f[l][r]]+1-p)*(a[f[l][r]]-p)/2,dfs(l,g[l][r]-1,p)+dfs(g[l][r]+1,r,p)+a[g[l][r]]-p);
encode(hash(l,r,p),res);
return res;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
f[i][i]=g[i][i]=i;
for(int j=i+1;j<=n;j++){
if(a[f[i][j-1]]<=a[j]){
f[i][j]=f[i][j-1];
}
else{
f[i][j]=j;
}
if(a[g[i][j-1]]>=a[j]){
g[i][j]=g[i][j-1];
}
else{
g[i][j]=j;
}
}
}
// for(int l=1;l<=n;l++)for(int r=l;r<=n;r++){
// cout<<f[l][r]<<" "<<g[l][r]<<"\n";
// }
cout<<dfs(1,n,0);
return 0;
}
}
int main(){return asbt::main();}

浙公网安备 33010602011771号