20251009 NOIP模拟赛
20251009 NOIP模拟赛
Problem A. 棒棒糖
\(x\) 向 \(2x+2\) 连边,形成若干条链。每一条链的贡献即为 \(\lceil \frac{len}{2}\rceil\)。
可以考虑先加上所有在链上排名 \(\ge 1\) 的元素,再减去排名 \(\ge 2\) 的,再加上 \(\ge 3\) 的……贡献可以快速求出,而链长不超过 \(O(\log n)\)。
int T; ll n;
signed main(){
read(T);
while(T--){
read(n);
ll ans=0;
for(ll x=1,y=1,z=1;x<=n;x=x+x+2,y<<=1,z=-z){
ll res=(n-x+y)/y;
ans+=res*z;
}
printf("%lld\n",ans);
}
return 0;
}
Problem B. 灯泡
一次操作不改变 \(x\) 轴上的奇偶性,也不改变直线 \(x+y=k\) 上的奇偶性。找到两条奇数直线,就可以知道 \(X,X+Y\)。
int n;
map<int,int> mp1,mp2;
signed main(){
read(n);
for(int i=1;i<=n;i++){
int x,y;
read(x),read(y);
++mp1[x],++mp2[x+y];
}
int X=0,Y=0;
for(auto i:mp1){
if(i.second&1)
X=i.first;
}
for(auto i:mp2){
if(i.second&1)
Y=i.first;
}
Y-=X;
printf("%d %d\n",X,Y);
return 0;
}
Problem D. 金币
首先设 \(f_{i,(x,y)}\) 表示 \(i\) 次操作后,两枚金币的位置为 \((x,y)\) 的方案数。\((x,y)\) 是无序对,认为 \((x,y)=(y,x)\)。转移平凡。
然后发现除了 \(X,Y\) 两个位置,其他位置的标号都是不重要的。于是状态可以分为三类:
- \(f_{(X,Y)}\);
- \(f_{(X,p)},f_{(Y,q)}\),\(p\ne X,p\ne Y,q\ne X,q\ne Y\);
- \(f_{(p,q)}\),\(p\ne X,p\ne Y,q\ne X,q\ne Y\)。
每一类的 dp 值都是相等的。所以设 \(F_i,G_i,H_i\) 为 \(i\) 次操作后三类 dp 值分别是多少。推一下转移矩阵:
矩阵快速幂,然后 \(O(n)\) 算最终答案。
int n,X,Y;
ll m;
const ll mod=998244353;
inline ll Mod(ll x){return (x>=mod)?(x-mod):(x);}
inline void Add(ll &x,ll y){x=Mod(x+y);}
struct Matrix{
ll val[4][4];
friend Matrix operator * (Matrix x,Matrix y){
Matrix z; memset(z.val,0,sizeof(z.val));
z.val[1][1]=(x.val[1][1]*y.val[1][1]+x.val[1][2]*y.val[2][1]+x.val[1][3]*y.val[3][1])%mod;
z.val[1][2]=(x.val[1][1]*y.val[1][2]+x.val[1][2]*y.val[2][2]+x.val[1][3]*y.val[3][2])%mod;
z.val[1][3]=(x.val[1][1]*y.val[1][3]+x.val[1][2]*y.val[2][3]+x.val[1][3]*y.val[3][3])%mod;
z.val[2][1]=(x.val[2][1]*y.val[1][1]+x.val[2][2]*y.val[2][1]+x.val[2][3]*y.val[3][1])%mod;
z.val[2][2]=(x.val[2][1]*y.val[1][2]+x.val[2][2]*y.val[2][2]+x.val[2][3]*y.val[3][2])%mod;
z.val[2][3]=(x.val[2][1]*y.val[1][3]+x.val[2][2]*y.val[2][3]+x.val[2][3]*y.val[3][3])%mod;
z.val[3][1]=(x.val[3][1]*y.val[1][1]+x.val[3][2]*y.val[2][1]+x.val[3][3]*y.val[3][1])%mod;
z.val[3][2]=(x.val[3][1]*y.val[1][2]+x.val[3][2]*y.val[2][2]+x.val[3][3]*y.val[3][2])%mod;
z.val[3][3]=(x.val[3][1]*y.val[1][3]+x.val[3][2]*y.val[2][3]+x.val[3][3]*y.val[3][3])%mod;
return z;
}
}I,A,B;
Matrix Quickpow(Matrix x,ll y){
Matrix res=I;
while(y){
if(y&1) res=res*x;
x=x*x; y>>=1;
}
return res;
}
signed main(){
read(n),read(m),read(X),read(Y);
if(X>Y) swap(X,Y);
I.val[1][1]=I.val[2][2]=I.val[3][3]=1;
A.val[1][1]=(1ll*(n-2)*(n-3)/2+1)%mod;
A.val[1][2]=1;
A.val[1][3]=0;
A.val[2][1]=2*(n-2);
A.val[2][2]=(1ll*(n-2)*(n-3)/2+n-1)%mod;
A.val[2][3]=4;
A.val[3][1]=0;
A.val[3][2]=n-3;
A.val[3][3]=(1ll*n*(n-1)/2-4+mod)%mod;
B.val[1][1]=1; B=B*Quickpow(A,m);
ll F=B.val[1][1],G=B.val[1][2],H=B.val[1][3];
ll ansf=F*Y%mod,ansg=0,ansh=0;
for(int i=1;i<=n;i++){
if(i==X) (ansg+=G*(X-1)%mod*i)%=mod;
else if(i==Y) (ansg+=G*(Y-2)%mod*i)%=mod;
else if(i>Y) (ansg+=G*2*i)%=mod;
else if(i>X) (ansg+=G*i)%=mod;
}
for(int i=1;i<=n;i++){
if(i==X||i==Y) continue;
else if(i>Y) (ansh+=H*(i-3)%mod*i)%=mod;
else if(i>X) (ansh+=H*(i-2)%mod*i)%=mod;
else (ansh+=H*(i-1)%mod*i)%=mod;
}
ll ans=(ansf+ansg+ansh)%mod;
printf("%lld\n",ans);
return 0;
}
Problem E. 糖果
设 \(b_i\) 为一种匹配方案中第 \(i\) 大的二元组。
拆贡献,\(b_i=\sum_{j\ge 1}[b_i\ge j]\)。枚举 \(j\),转化为求 \(b_i\ge j\) 的方案总数。
\(b_i\ge J\) 又等价于至少 \(n-i+1\) 个二元组 \(\ge J\)。
设 \(F_i\) 为恰好有 \(i\) 个二元组 \(\ge J\) 的方案数,\(G_i\) 为钦定 \(i\) 个二元组 \(\ge J\) 的方案数。
容易得到,
二项式反演,
接下来求 \(G_i\)。先将 \(a\) 升序排序,枚举 \(i\),\(k\) 能和其匹配当且仅当 \(k<i\land a_k+a_i\ge J\)。对应的 \(k\) 是个区间,且 \([l_i,r_i]\subseteq[l_{i+1},r_{i+1}]\)。
设 \(f_{i,j}\) 为考虑前 \(i\) 个元素,已经钦定匹配 \(j\) 个的方案数。
- \(f_{i,j}\leftarrow f_{i-1,j}\);
- \(f_{i,j+1}\leftarrow f_{i-1,j}\times (r_i-l_i+1-2j)\),\(r_i-l_i+1-2j>0\)。
然后 \(G_i=f_{2n,i}\times \frac{(2n-2i)!}{(n-i)!2^{n-i}}\)。
\(b_i\) 只有 \(O(n^2)\) 种取值,所以可以只枚举 \(O(n^2)\) 个 \(j\)。总复杂度 \(O(n^4)\)。
const ll mod=998244353,inv2=(mod+1)>>1;
inline ll Mod(ll x){return (x>=mod)?(x-mod):(x);}
inline void Add(ll &x,ll y){x=Mod(x+y);}
int n,m; ll a[N],c[N*N];
ll C[N][N],fac[N],pw[N],caf[N];
ll f[N][N>>1],F[N],G[N],H[N],l[N];
ll Quickpow(ll x,ll y){
ll res=1;
while(y){
if(y&1) res=res*x%mod;
x=x*x%mod; y>>=1;
}
return res;
}
void Init(){
fac[0]=caf[0]=1;
for(int i=1;i<=n+n;i++) fac[i]=fac[i-1]*i%mod;
caf[n]=Quickpow(fac[n],mod-2);
for(int i=n-1;i;i--) caf[i]=caf[i+1]*(i+1)%mod;
pw[0]=1;
for(int i=1;i<=n;i++) pw[i]=pw[i-1]*inv2%mod;
C[0][0]=1;
for(int i=1;i<=n;i++){
C[i][0]=1;
for(int j=1;j<=i;j++)
C[i][j]=Mod(C[i-1][j-1]+C[i-1][j]);
}
}
void Solve(int x){
for(int i=1,j=0;i<=n+n;i++){
l[i]=lower_bound(a+1,a+n+n+1,c[x]-a[i])-a;
}
memset(f,0,sizeof(f));
f[0][0]=1;
for(int i=1;i<=n+n;i++){
for(int j=0;j<=n;j++){
if(!f[i-1][j]) continue;
Add(f[i][j],f[i-1][j]);
if(i-l[i]-j-j>0) (f[i][j+1]+=f[i-1][j]*(i-l[i]-j-j))%=mod;
}
}
for(int i=0;i<=n;i++) G[i]=f[n+n][i]*fac[n+n-i-i]%mod*caf[n-i]%mod*pw[n-i]%mod;
for(int i=0;i<=n;i++){
F[i]=0;
for(int j=i;j<=n;j++){
ll res=G[j]*C[j][i]%mod;
if((j-i)&1) Add(F[i],mod-res);
else Add(F[i],res);
}
}
for(int i=n;i>=0;i--) Add(F[i],F[i+1]);
for(int i=1;i<=n;i++) (H[i]+=F[n-i+1]*(c[x]-c[x-1]))%=mod;
}
signed main(){
read(n);
Init();
for(int i=1;i<=n+n;i++) read(a[i]);
sort(a+1,a+n+n+1);
for(int i=1;i<=n+n;i++){
for(int j=1;j<i;j++)
c[++m]=a[i]+a[j];
}
sort(c+1,c+m+1);
for(int i=1;i<=m;i++){
Solve(i);
}
for(int i=1;i<=n;i++) printf("%lld ",H[i]);
puts("");
return 0;
}
Problem F. 跳跃
移动 \(i\),维护 \(i\) 到 \(j\) 的最短路。
让 \(i\) 从 \(n\) 移动到 \(1\)。当 \(i+1\) 移动到 \(i\) 时,\([i+1,v_i-1]\) 中的 \(dis\) 都要加一,\([v_i,n]\) 中的 \(dis\) 都要加上 \(-dis_{v_i}+1\)。
区间加,全局查询 \(\le K\) 的个数,分块直接做到 \(O(n\sqrt{n\log n})\)。
但由于 \(K\) 固定,且 \(dis\) 变化量不超过 \(O(n)\),可以在每个块内开桶,拿一个指针扫,每个块内指针移动次数不超过 \(O(n)\),所以做到 \(O(n\sqrt{n})\)。
int n,K,v[N];
int a[N],C;
const int B=500,D=615;
int lp[D],rp[D],bl[N],pos[D],tag[D],cnt[D],ans[N];
short buc[D][N];
void Flush(int p){
while(pos[p]+tag[p]<K&&pos[p]<n){
++pos[p];
cnt[p]+=buc[p][pos[p]];
}
while(pos[p]+tag[p]>K&&pos[p]>0){
cnt[p]-=buc[p][pos[p]];
--pos[p];
}
}
void Update(int p,int l,int r,int v){
cnt[p]=0;
for(int i=lp[p];i<=rp[p];i++){
if(a[i]<=n) --buc[p][a[i]];
a[i]+=tag[p];
if(l<=i&&i<=r) a[i]+=v;
if(a[i]<=n) ++buc[p][a[i]];
cnt[p]+=a[i]<=K;
}
tag[p]=0,pos[p]=K;
}
void Update(int l,int r,int v){
if(l>r) return;
int p=bl[l],q=bl[r];
if(p==q) return Update(p,l,r,v);
Update(p,l,rp[p],v);
Update(q,lp[q],r,v);
for(int i=p+1;i<=q-1;i++) tag[i]+=v;
}
int Ask(int l,int r){
int p=bl[l],q=bl[r];
if(p==q){
int res=0;
for(int i=l;i<=r;i++) res+=(tag[p]+a[i]<=K);
return res;
}
int res=0;
for(int i=l;i<=rp[p];i++) res+=(tag[p]+a[i]<=K);
for(int i=lp[q];i<=r;i++) res+=(tag[q]+a[i]<=K);
for(int i=p+1;i<=q-1;i++){
Flush(i);
res+=cnt[i];
}
return res;
}
signed main(){
read(n),read(K);
for(int i=1;i<n;i++) read(v[i]);
memset(a,0x3f,sizeof(a));
for(int i=1;i<=n;i+=B){
++C; lp[C]=i;
rp[C]=min(n,i+B-1);
for(int j=lp[C];j<=rp[C];j++) bl[j]=C;
}
for(int i=n;i;i--){
++buc[bl[i]][0]; ++cnt[bl[i]]; a[i]=0;
if(i!=n){
Update(i+1,v[i]-1,1);
int w=a[v[i]]+tag[bl[v[i]]];
Update(v[i],n,-w+1);
}
ans[i]=Ask(i,n);
}
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
puts("");
return 0;
}

浙公网安备 33010602011771号