【比赛记录】2025CSP-S模拟赛3

A. 光
首先有一个贪心,每次选择需要电量最大的位置加上 \(4\),给相邻的两个位置加上 \(2\),再给对角位置加上 \(1\)。
这样不正确的原因是最后一些位置可能剩不下 \(4\) 个电量了,而我们四个四个放,就会产生浪费。那么我们之间暴力枚举最后每个位置都剩下多少(\(0\) 到 \(3\)),然后再贪心即可。时间复杂度 \(O(256(a+b+c+d))\)。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int inf=0x3f3f3f3f;
int A,B,C,D;
il int solve(int a,int b,int c,int d){
int res=0;
while(a>0||b>0||c>0||d>0){
res++;
int tmp=max({a,b,c,d});
if(a==tmp){
a-=4,b-=2,c-=2,d--;
}
else if(b==tmp){
b-=4,a-=2,d-=2,c--;
}
else if(c==tmp){
c-=4,a-=2,d-=2,b--;
}
else{
d-=4,b-=2,c-=2,a--;
}
}
return res<<2;
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>A>>B>>C>>D;
int ans=inf;
for(int a=0;a<=3;a++){
for(int b=0;b<=3;b++){
for(int c=0;c<=3;c++){
for(int d=0;d<=3;d++){
ans=min(ans,a+b+c+d+solve(A-a-b/2-c/2-d/4,B-b-a/2-d/2-c/4,C-c-a/2-d/2-b/4,D-d-b/2-c/2-a/4));
}
}
}
}
cout<<ans;
return 0;
}
}
int main(){return asbt::main();}
B. 爬
对于每个点,我们考虑按位拆贡献。对于当前位,枚举有多少个为 \(1\) 的儿子节点爬上来,简单组合数计算方案数即可。注意当这个点上只有一个蚂蚁时不算贡献,还有要特判根。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e5+5,mod=1e9+7;
int n,a[maxn],fa[maxn];
int _2[maxn],fac[maxn],inv[maxn];
vector<int> e[maxn];
il int qpow(int x,int y){
int res=1;
while(y){
if(y&1){
res=res*1ll*x%mod;
}
x=x*1ll*x%mod,y>>=1;
}
return res;
}
il int C(int x,int y){
if(x<y||y<0){
return 0;
}
return fac[x]*1ll*inv[y]%mod*inv[x-y]%mod;
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
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=2;i<=n;i++){
cin>>fa[i];
e[fa[i]].pb(i);
}
_2[0]=1;
for(int i=1;i<=1e5;i++){
_2[i]=_2[i-1]*2ll%mod;
}
fac[0]=1;
for(int i=1;i<=n;i++){
fac[i]=fac[i-1]*1ll*i%mod;
}
inv[n]=qpow(fac[n],mod-2);
for(int i=n;i;i--){
inv[i-1]=inv[i]*1ll*i%mod;
}
int ans=0;
for(int i=1,tot;i<=n;i++){
tot=e[i].size();
for(int j=0,cnt;j<=30;j++){
cnt=0;
for(int u:e[i]){
if(a[u]>>j&1){
cnt++;
}
}
for(int k=0;k<=cnt;k++){
if(k==0){
if(a[i]>>j&1){
(ans+=(_2[tot-cnt]-1+mod)*1ll*_2[n-tot-(i==1?1:2)]%mod*_2[j]%mod)%=mod;
}
}
else if(k==1){
if(a[i]>>j&1){
if(i!=1){
(ans+=cnt*1ll*(_2[tot-cnt]-1+mod)%mod*_2[n-tot-2]%mod*_2[j]%mod)%=mod;
}
}
else{
(ans+=cnt*1ll*_2[tot-cnt]%mod*_2[n-tot-(i==1?1:2)]%mod*_2[j]%mod)%=mod;
if(i!=1){
(ans+=cnt*1ll*(_2[tot-cnt]-1+mod)%mod*_2[n-tot-2]%mod*_2[j]%mod)%=mod;
}
}
}
else if(k&1){
if(a[i]>>j&1){
if(i!=1){
(ans+=C(cnt,k)*1ll*_2[tot-cnt]%mod*_2[n-tot-2]%mod*_2[j]%mod)%=mod;
}
}
else{
(ans+=C(cnt,k)*1ll*_2[tot-cnt]%mod*_2[n-tot-1]%mod*_2[j]%mod)%=mod;
}
}
else{
if(a[i]>>j&1){
(ans+=C(cnt,k)*1ll*_2[tot-cnt]%mod*_2[n-tot-(i==1?1:2)]%mod*_2[j]%mod)%=mod;
}
}
}
}
}
cout<<ans;
return 0;
}
}
int main(){return asbt::main();}
/*
5
64 19 1 46 1
1 1 1 1
*/
C. 字符串
考虑 \(c\) 的限制非常的讨厌,数据范围是允许我们去 \(O(n)\) 枚举的,所以我们首先就枚举 \(\underbrace{BBB\dots B}_{c}A\underbrace{BBB\dots B}_{c}\) 这样的有几段。然后我们把剩下的 \(A\) 和 \(B\) 填充进去。
首先填 \(A\),显然可以在最开头填一个,产生 \(1\) 的贡献。然后本质上就是每填进去长为 \(a\) 的 \(A\) 序列,就产生 \(1\) 的贡献。
然后填 \(B\),如果当前最后一个字母是 \(A\),那显然可以在最后面添加一个 \(B\) 来产生 \(1\) 的贡献。然后我们把所有长为 \(c\) 的 \(B\) 串都填成 \(k\cdot b+1,k\in\mathbb{Z}\) 的长度。此时再多一段长为 \(b\) 的 \(B\) 串就会再产生 \(1\) 的贡献。不要忘了如果 \(c>b\) 那么一开始就会有贡献。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
int T,n,m,a,b,c;
il void solve(){
cin>>n>>m>>a>>b>>c;
int ans=0;
for(int i=0;;i++){ // BBB/A 有几段
int cnta=i/2,cntb=(i+1)/2;
int A=n-cnta,B=m-cntb*c;
if(A<0||B<0){
break;
}
int res=i;
res+=(c-(c-1)%b-1)/b*cntb;
if(A>0){
A--,res++;
}
res+=A/a;
if(i%2==0&&B>0){
B--,res++;
}
int tmp=((1-c)%b+b)%b;
if(tmp){
int num=min(B/tmp,cntb);
res+=num,B-=num*tmp;
}
res+=B/b;
ans=max(ans,res);
}
cout<<ans<<"\n";
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>T;
while(T--){
solve();
}
return 0;
}
}
int main(){return asbt::main();}
D. 奇怪的函数
首先,这个函数其实显然是一个分三段的连续的函数,形如:
于是我们维护函数的 \(L\),\(R\) 和 \(D\),即可通过没有修改的那个包。维护这三个值是可以线性完成的。
考虑一次修改后我们就要再整个扫一遍,无法接受。考虑分块,那么每次修改重构一个块,查询就去扫所有块,单次时间都是 \(O(\sqrt{n})\) 的。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e5+5,maxb=320,inf=1e18;
int n,m,blen,bnum,bel[maxn],st[maxb],ed[maxb];
struct{
int opt,val;
}a[maxn];
struct{
int L,R,D;
}b[maxb];
il void upd(int x){
b[x].L=-inf,b[x].R=inf,b[x].D=0;
for(int i=st[x];i<=ed[x];i++){
switch(a[i].opt){
case 1:{
b[x].L+=a[i].val;
b[x].R+=a[i].val;
b[x].D+=a[i].val;
break;
}
case 2:{
b[x].L=min(b[x].L,a[i].val);
b[x].R=min(b[x].R,a[i].val);
break;
}
default:{
b[x].L=max(b[x].L,a[i].val);
b[x].R=max(b[x].R,a[i].val);
break;
}
}
}
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].opt>>a[i].val;
}
blen=sqrt(n);
bnum=(n+blen-1)/blen;
for(int i=1;i<=bnum;i++){
st[i]=ed[i-1]+1;
ed[i]=min(ed[i-1]+blen,n);
for(int j=st[i];j<=ed[i];j++){
bel[j]=i;
}
upd(i);
}
cin>>m;
while(m--){
int opt,x,val;
cin>>opt>>x;
if(opt==4){
for(int i=1;i<=bnum;i++){
x+=b[i].D;
x=min(x,b[i].R);
x=max(x,b[i].L);
}
cout<<x<<"\n";
}
else{
cin>>val;
a[x].opt=opt,a[x].val=val;
upd(bel[x]);
}
}
return 0;
}
}
signed main(){return asbt::main();}

浙公网安备 33010602011771号