NOIP 2025 订正
前言
95+40+4+5=144 pts,wssb
NOIP 后两个小时加起来拿了 9 分的高分,不如冲 T2。
T1 Candy
简单题,忘记特判钱是否够痛失 5 分。
T2 Sale
考场上大概有些思路,但当时在发烧,脑子比较混乱,故去打 T3,T4 暴力,然后,就没有然后了,比赛结束了。。。
考虑容斥将其转换成总方案减去不是最优的方案。
可以先考虑 \(m=2\) 的部分分,我觉得是具有启发性的。
若 \(w_1=1\) 肯定是最优的,不符合条件。
当 \(w_1=2\) 时需要考虑什么时候会出现不优的情况。样例告诉我们:因为是按性价比排序,因此可能会出现一个 \(i\) 满足 \(w_i=1\) 且 \(a_i>\frac{a_1}{2}\),此时会先去买第 \(i\) 件物品而导致买不起第 \(1\) 件物品,从而去买第一个 \(w_j=1\) 的物品或是没有买的起的物品。若 \(a_i+a_j<a_1\),则此时不是最优的,因为可以直接去买第 \(1\) 件物品。
可以枚举满足条件的 \(i,j\),从而计算答案。此时会有 \(i\) 前面所有物品的 \(w\) 均为 \(2\),\(w_{i+1\sim j-1}=2\),此时 \(j+1\sim n\) 的值可以任意取,因此贡献为 \(2^{n-j}\)。
或是只有一个 \(i\) 此时除 \(i\) 的 \(w\) 均为 \(2\),贡献为 \(1\)。
代码长这样:
ll tp=0;
for(int i=2;i<=n;i++){
if(a[i]*2<=a[1]) break;
if(a[i]^a[1]) tp++;
while(tp>=mod) tp-=mod;
for(int j=i+1;j<=n;j++){
if(a[i]+a[j]<a[1]) tp=(tp+K[n-j])%mod;
}
}
ll ans=(K[n]-tp)%mod+mod;
if(ans>=mod) ans-=mod;
注意要特判 \(a_i=a_1\) 的情况。
正解:
会发现最后一定是最后买的物品出现不优的情况,就像 \(m=2\) 一样。
可以枚举 \(i,j\) 表示最后买 \(i\) 的物品是最优的,但去买了 \(j\) 物品。此时有两种情况:
-
只有 \(w_j=1\),此时可以枚举 \(w_k=2(k<i)\) 的个数,贡献为 \(\sum\limits_{p=0}^{i-1} \binom{i-1}{p}\binom{j-i-1}{m-p-i-1}=\binom{j-2}{m-i-1}\)。(范德蒙德卷积)
-
存在 \(w_k=2\),可以枚举 \(k\),有 \(w_{j+1\sim k-1}=2\),有贡献为 \(2^{n-k}\times \binom{j-2}{m-i-1}\)。
复杂度为 \(\mathcal{O(n^3)}\)。
发现可以拿指针维护最大满足条件的 \(k\),则贡献为 \(\binom{j-2}{m-i-1}+\sum\limits_{x=k}^n2^{n-x}\binom{j-2}{m-i-1}=2^{n-k+1}\binom{j-2}{m-i-1}\)。
要预处理 \(2\) 的幂次。
Code
点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define PLL pair<ll,ll>
#define PII pair<int,int>
//#define Tp template<typename T>
//#define Ts template<typename T,typename ...args>
#define lb(x) ((x)&(-x))
using namespace std;
const int N=5e3+20,M=1e5+20,MX=5000;
const ll INF=1ll<<60,mod=998244353;
namespace H_H{
int n,m;
ll a[N];
ll pre[N],prn[N],K[N];
inline ll qpow(ll x,ll y){
ll Ans=1;
for(;y;y>>=1,x=x*x%mod) if(y&1) Ans=Ans*x%mod;
return Ans;
}
inline ll binom(ll x,ll y){
if(x<y) return 0;
return pre[x]*prn[y]%mod*prn[x-y]%mod;
}
inline void init(){
K[0]=1;
for(int i=1;i<=MX;i++) K[i]=K[i-1]*2%mod;
pre[0]=prn[0]=1;
for(int i=1;i<=MX;i++) pre[i]=pre[i-1]*i%mod;
prn[MX]=qpow(pre[MX],mod-2);
for(int i=MX-1;i;i--) prn[i]=prn[i+1]*(i+1)%mod;
}
inline void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n,greater<>());
ll ans=0;
for(int i=1;i<=n;i++){
if(m-i-1<0) break;
ll k=n+1;
for(int j=i+1;j<=n;j++){
if(a[i]==a[j]) continue;
if(a[j]*2<=a[i]) break;
while(k-1>j && a[k-1]+a[j]<a[i]) k--;
ans=(ans+K[n-k+1]*binom(j-2,m-i-1)%mod)%mod;
}
}
cout<<(K[n]-ans+mod)%mod<<"\n";
}
int main(){
init();int C=0,T=1;
cin>>C>>T;
while(T--) solve();
return 0;
}
}
int main(){
IOS;H_H::main();
return 0;
}
T3
T4
\(qn^2\log n\) 跑不过 \(qn^3\)。这就是人傻常数大吗?
好题。
先求前缀和,为 \(a_{0\dots n-1}\)。
考虑固定 \(r\) 求所有 \(l\) 造成的贡献。
由于区间长度为 \([L,R]\),因此 \(l\in [r-R,r-L]\)。可以维护一个 \(a\) 递减的序列 \(q_1,\dots,q_k\)。
对于序列里的两个位置 \(q_i,q_{i+1}\)。对于答案 \((q_i,q_{i+1}]\) 的贡献为 \(a_r-a_{q_i}\),序列的最后一个数对答案 \((q_k,r]\) 的贡献为 \(a_r-a_{q_k}\)(贡献即为对 ans 取 max)。可以分别计算贡献。
-
维护序列最后一个数的贡献:可以倒着枚举 \(r\),会有 \(q_k\) 是不增的,用 ST 表维护 \(q_k\),单调队列维护 \(q_k\) 到 \(r\) 的贡献,可以看代码。
-
维护序列中间的贡献:会有 \(q_{i+1}\) 为 \(q_i\) 右边第一个 \(a\) 比它小的数,可以单调栈预处理。对于 \(i\),设其右边第一个比它小的数的位置为 \(nxt_i\)(没有则 \(nxt_i=n\))。可以看做对于区间 \((i,nxt_i]\) 有 \(\max\limits_{r,r-L\ge nxt_i \wedge r-R\le i} a_r-a_i=\max\limits_{r=nxt_i+L}^{i+R} a_r-a_i\) 可以拿 ST 表维护。可是每一个点会被很多个区间覆盖,最简单的方法是拿线段树维护,但会 TLE。
会发现每个区间的关系为包含但不交,因此可以把区间看成编号为 \(i+1\) ,权值为这个区间的贡献的点,\(i+1\) 到 \(j+1\) 有一条边,其中 \(j\) 为最大的满足 \((j,nxt_j]\) 包含 \((i,nxt_i]\)。那么 \(i\) 点的答案为它所有祖先的权值的 \(\max\) 遍历一遍树就可以了。
Tip:可以不用建树,对于 \(i\) 点它父亲的编号一定比它小,可以用类似于 dp 的方式处理。
Code
点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define fi first
#define se second
//#define Tp template<typename T>
//#define Ts template<typename T,typename ...args>
#define lb(x) ((x)&(-x))
using namespace std;
const int N=5e4+20,M=1e5+20;
const ll INF=1ll<<60,mod=998244353;
namespace H_H{
int n,m,ff[N],pre[N],mn[N][17],nxt[N];
ll a[N],ans[N],ans1[N],ans2[N];
ll K[N],mx[N][17];
set<PII> s;
inline int get(int x,int y){
if(x>y) swap(x,y);
return a[x]<=a[y]?x:y;
}
inline int qmn(int l,int r){
while(l>r) return -1;
int k=K[r-l+1];
return get(mn[l][k],mn[r-(1<<k)+1][k]);
}
inline ll qmx(int l,int r){
while(l>r) return -INF;
int k=K[r-l+1];
return max(mx[l][k],mx[r-(1<<k)+1][k]);
}
int st[N],top;
PII b[N];
inline void init(){
ans1[0]=-INF;
for(int i=1;i<=n;i++) a[i]+=a[i-1];
for(int i=2;i<=n+1;i++) K[i]=K[i>>1]+1;
for(int i=1;i<=n;i++){
mn[i][0]=i;
mx[i][0]=a[i];
}
for(int j=1;j<=K[n];j++){//ST 表
for(int i=1;i+(1<<j)-1<=n;i++){
mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
}
}
for(int j=1;j<=K[n+1];j++){
for(int i=0;i+(1<<j)-1<=n;i++){
mn[i][j]=get(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
}
}
st[0]=n;
for(int i=n-1;~i;i--){//单调栈
while(top && a[st[top]]>a[i]) top--;
nxt[i]=st[top];st[++top]=i;
b[i]={i+1,nxt[i]};
}
for(int i=n-1;~i;i--){//找到第一个把它覆盖的点
int r=nxt[i];
auto itt=s.upper_bound({r,n+1});
for(auto it=s.begin();it!=itt;it++) ff[(*it).second]=i+1;
s.erase(s.begin(),itt);s.insert({r,i+1});
}
}
inline void Init(){
for(int i=1;i<=n;i++){
ans[i]=ans1[i]=ans2[i]=-INF;
pre[i]=0;
}
}
inline void get_ans(){
for(int i=1;i<=n;i++){//与其祖先取 max
ans1[i]=max(ans1[i],ans1[ff[i]]);
}
for(int i=1;i<=n;i++){
ans[i]=max(ans1[i],ans2[i]);
}
}
int fr,tl;
PLL q[N];
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
init();
cin>>m;
for(int tt=1,L,R;tt<=m;tt++){
cin>>L>>R;
Init();
fr=1,tl=0;
for(int i=n;i;i--){
if(i>=L){
int pos=qmn(max(0,i-R),i-L);
ll tp=a[i]-a[pos];
while(fr<=tl && q[tl].se<=tp) tl--;//单调队列维护
q[++tl]={pos+1,tp};
}
while(fr<=tl && q[fr].fi>i) fr++;
if(fr<=tl) ans2[i]=q[fr].se;
}
for(int i=0;i<n;i++){
ans1[i+1]=qmx(nxt[i]+L,min(n,i+R))-a[i];
}
get_ans();
unsigned ll Ans=0;
for(int i=1;i<=n;i++) Ans^=ans[i]*i;
cout<<Ans<<"\n";
}
return 0;
}
}
int main(){
IOS;H_H::main();
return 0;
}

浙公网安备 33010602011771号