[NOI 2026] 中位数
我声称摩卡串是简单好写好想题。
题目链接: [NOI 2026] 中位数 。
题目描述:
把长度为 n 的序列切成 k 段,要求每段中位数构成的序列的中位数最大。
5≤n≤106,2≤k≤n 。
中位数的定义:长度为 m 的序列的中位数为第 ⌈2m⌉ 大的数。
警示后人:
性质 A , kmod2≡0 和 k>5 是诈骗性质。
这个题目就是让你冲正解的,几乎没有部分分。
经典二分转 1,−1 序列 b 。
那么一个序列的中位数 ≥x 的充要条件为序列的和 ≥0 。
现在问题变成了:判定一个 −1,1 的序列 b 是否能切成 k 份使得至少有 ⌈2k⌉ 个子区间的和 ≥0 。
首先,我们有一个显然的构造:选取 ⌈2k⌉ 个 1 单独成一个子区间,然后序列就会断成 k+(kmod2)+1 个区间。
接着我们就要根据 kmod2 分类讨论:
Case #1 : kmod2=0
此时我们需要删除一个区间。
考虑什么情况下我们可以删除区间。
由于序列能断成 k+(kmod2)+1 个区间的时候,他的样子应该是:
01010101010101010⋯0101010101010101010101010
那么我们就有三种方式删除区间:
- 让开头变为 1 ,即 10101010⋯010101010 。
- 让结尾变为 1 ,即 010101010⋯01010101 。
- 让中间两个 1 相邻,即 0101010⋯0101011010101⋯0101010 。
接下来考虑如何处理这三种情况。
首先第一第二种是容易的,只需要枚举一个前后缀,然后判断剩余区间的 1 的个数是否合法就行。
然后就是第三种。这种情况下我们可以枚举一个 i ,预处理出 posi 表示最大的满足 [posi,i] 的和 ≥0 的数, pos2i 表示最小的满足 [i,pos2i] 的和 ≥0 的数。用 posi 和 pos2i+1 转移就行。
问题是如何线性预处理 pos,pos2 。
以 pos 举例。
理论上,我们为了处理这个,需要整一个值域数组,每次单点修改,以及查询一个后缀的最大值。这似乎只能 O(logn) 做。但是,这是一个 −1,1 序列。这也就意味着,对于某一个 i ,若 bi=1 ,则 posi=i ,否则必然有 [posi,i] 的和为 0 。此时就是一个单点修改,单点查询,一个桶搞定。
Case #2 : kmod2=1
此时要删两个区间。
那么,将 Case #1 的三种删除区间的方法组合以下,可以得到以下七种:
- 两端都是 1 ,即 101010⋯01010101 。
- 开头的连续两个 1 ,即 1101010⋯010101010 。
- 结尾的连续两个 1 ,即 010101010⋯0101011 。
- 开头一个 1 ,中间连续两个 1 ,即 10101010⋯0101011010101⋯0101010 。
- 结尾一个 1 ,中间连续两个 1 ,即 0101010⋯0101011010101⋯01010101 。
- 中间连续 3 个 1 ,即 0101010⋯01010111010101⋯0101010 。
- 两个无交的连续的两个 1 ,即 0101010⋯0101011010101⋯0101011010101⋯0101010 。
前 5 种利用 pos,pos2 容易转移。
第 7 种利用一个辅助数组也可以轻松转移。
主要是第 6 种。
首先一个直观的想法是枚举 i ,然后令三个区间分别为 [posposi−1,posi−1][posi,i][i+1,pos2i+1] 。
然而这是错的。
举一个例子, 100101 ,有一个显然的划分为 (10)(01)(01) ,但是如果套用刚才的那个算法就会挂掉。这是因为最短的 ≥0 的区间的另一个端点不一定也有对应的 ≥0 的区间。换句话说,也就是,可能 posposi−1 不存在但是存在一个更小的 q 满足 [posq−1,q−1][q,i] 合法。
解决它也十分简单。令 pi 为最大的满足 [p,i] 的和恰好为 0 的数。那么枚举 i ,令三个区间先分别为 [posposi−1,posi−1][posi,i][i+1,pos2i+1] ,再分别为 [pospi−1,pi−1][pi,i][i+1,pos2i+1] 。
原因十分简单,如果存在一个更小的 q 满足 q<pi 且 posq−1 存在且 pospi−1 不存在,不妨设 pi<posi ,那么必然有 [q,i] 的和为 0 。又因为 [p,i] 的和也为 0 ,所以 [q,p−1] 的和为 0 ,也就是说, pi 必然存在且至少为 q ,矛盾,所以原假设不成立,考虑 posi 和 pi 可以涵盖所有情况。
以上是整个题目最难的部分(不是就这?),大力出奇迹的分类讨论就行。
代码:
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
| #include <bits/stdc++.h> #include "median.h" using namespace std; #define WZH #ifdef WZH #ifndef MEDIAN_H void init(int c, int t); int median(int n, int k, std::vector<int> a); #endif #endif using V=vector<int>; bool check(V &a,int x,int k){ int n=a.size(),aim=(k+1)>>1; V b(n+1,0),s(n+1,0),S(n+1,0); for(int i=1;i<=n;i++)b[i]=(a[i-1]>=x?1:-1); for(int i=1;i<=n;i++)s[i]=s[i-1]+b[i],S[i]=S[i-1]+(b[i]==1); if(S[n]<aim)return 0; V cl(n*2+1,0),cl2(n*2+1,0),pos(n+1,0),pos2(n+1,0),p(n+1,0),pp(n+1,n+1); for(int i=1;i<=n;i++){ if(b[i]==1)pos[i]=i; else pos[i]=cl[n+s[i]]; p[i]=cl[n+s[i]]; cl[n+s[i]]=i+1; } for(int i=n;i>=1;i--){ if(b[i]==1)pos2[i]=i; else pos2[i]=cl2[n+s[n]-s[i-1]]; cl2[n+s[n]-s[i-1]]=i-1; } if(k&1){ int st=0,ed=0; for(int i=1;i<=n;i++)if(s[i]>=0){st=i;break;} for(int i=n;i>=1;i--)if(s[n]-s[i-1]>=0){ed=i;break;} if(st&&ed&&ed-st-1>=k-2&&S[ed-1]-S[st]>=aim-2)return 1; for(int i=1;i<n;i++){ if(pos2[i+1]&&s[i]>=0&&n-pos2[i+1]>=k-2&&S[n]-S[pos2[i+1]]>=aim-2) return 1; } for(int i=2;i<=n;i++){ if(pos[i-1]&&s[n]-s[i-1]>=0&&pos[i-1]-1>=k-2&&S[pos[i-1]-1]>=aim-2) return 1; } if(k==3)return 0; for(int i=1;i<n;i++){ if(!pos[i]||!pos2[i+1])continue; if(st&&pos[i]>st&&pos[i]-1-st+(n-pos2[i+1])>=k-3&& S[pos[i]-1]-S[st]+(S[n]-S[pos2[i+1]])>=aim-3) return 1; if(ed&&pos2[i+1]<ed&&ed-1-pos2[i+1]+(pos[i]-1)>=k-3&& S[pos[i]-1]+(S[ed-1]-S[pos2[i+1]])>=aim-3) return 1; } for(int i=2;i<n;i++){ int z=pos2[i+1],y=p[i]; if(!y||!z)continue; int x=pos[y-1]; if(!x)continue; if(x-1+n-z>=k-3&&S[x-1]+S[n]-S[z]>=aim-3)return 1; } for(int i=2;i<n;i++){ int z=pos2[i+1],y=pos[i]; if(!y||!z)continue; int x=pos[y-1]; if(!x)continue; if(x-1+n-z>=k-3&&S[x-1]+S[n]-S[z]>=aim-3)return 1; } if(k==5)return 0; for(int i=1;i<n;i++){ pp[i]=min(pp[i],pp[i-1]); if(!pos[i]||!pos2[i+1])continue; if(pp[pos[i]-1]<=pos[i]-1){ if(S[pos[i]-1]-pp[pos[i]-1]+S[n]-S[pos2[i+1]]>=aim-4) return 1; } pp[pos2[i+1]]=min(pp[pos2[i+1]],S[pos2[i+1]]-S[pos[i]-1]); } return 0; } else{ for(int i=1;i<=n-k+1;i++)if(s[i]>=0&&S[n]-S[i]>=aim-1)return 1; for(int i=k-1;i<n;i++)if(s[n]-s[i]>=0&&S[i]>=aim-1)return 1; if(k==2)return 0; for(int i=1;i<n;i++){ if(pos[i]&&pos2[i+1]&&pos[i]-1+n-pos2[i+1]>=k-2&& S[pos[i]-1]+S[n]-S[pos2[i+1]]>=aim-2) return 1; } return 0; } } void init(int c,int t){} int median(int n,int k,V a){ int l=1,r=n; while(l<r){ int mid=(l+r+1)>>1; if(check(a,mid,k))l=mid; else r=mid-1; } return l; }
|