专栏文章
题解:CF1630B Range and Partition
CF1630B题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miqbx2af
- 此快照首次捕获于
- 2025/12/04 02:17 3 个月前
- 此快照最后确认于
- 2025/12/04 02:17 3 个月前
非常妙的好题。
首先容易发现 满足要求的必要条件是:在 中的数的个数比不在 的数的个数至少多 。(每个区间都至少多一个)。可以用二分找出最小的 。下面我们都假定已经求出了 和 。
然后我们需要发现一个结论:这个必要条件同时也是充分条件。
证明:我们令 ,然后 。易知 ,那么由离散的介值定理得必然存在 ,即为长度为 的严格上升子序列,且可以通过调整使得最后一个元素一定为 。那么每一段的和均 ,满足要求,证毕。
接下来就简单了。我们找到一个长度为 的严格上升子序列,最后一个元素一定为 。这件事情很简单,只要去掉 ,把前面的小于 的数拿出来找一个长度为 的上升子序列即可。
CPPbool check(int x){
for (int i=1;i+x<=n;i++){
int j=i+x;
int cnt=sum[j]-sum[i-1];
int la=n-cnt;
if (cnt-la>=k) return 1;
}
return 0;
}
struct node{
int x,id;
}b[N],dp[N];
void sol(){
cin>>n>>k;
for (int i=1;i<=n;i++) sum[i]=0;
for (int i=1;i<=n;i++) cin>>a[i],sum[a[i]]++;
for (int i=1;i<=n;i++) sum[i]+=sum[i-1];
int L=0,R=n-1,Ans=0;
while (L<=R){
int mid=(L+R)>>1;
if (check(mid)) Ans=mid,R=mid-1;
else L=mid+1;
}// 二分找出 x 和 y
int l=0,r=0;
for (int i=1;i+Ans<=n;i++){
int j=i+Ans;
int cnt=sum[j]-sum[i-1];
int la=n-cnt;
if (cnt-la>=k){
l=i,r=j;
break;
}
}
cout<<l<<' '<<r<<endl;
for (int i=1;i<=n;i++){
if (l<=a[i]&&a[i]<=r) sum[i]=1;
else sum[i]=-1;
}
for (int i=1;i<=n;i++) sum[i]+=sum[i-1];
int x=sum[n];
int m=0;
for (int i=1;i<=n;i++){
if (sum[i]<x&&sum[i]>0) b[++m]={sum[i],i};//把前面的小于 sum[n] 的数拿出来
}
int cnt=0;
for (int i=1;i<=m;i++){
if (dp[cnt].x<b[i].x) dp[++cnt]=b[i];
else{
int L=0,R=cnt+1,Ans=0;
while (L<=R){
int mid=(L+R)>>1;
if (dp[mid].x>=b[i].x) Ans=mid,R=mid-1;
else L=mid+1;
}
if (b[i].x<dp[Ans].x) dp[Ans]=b[i];
}
}//求 LIS
vector<int> ans;
for (int i=1;i<k;i++) ans.pb(dp[i].id);
ans.pb(n);
int la=1;
for (int x:ans){
cout<<la<<' '<<x<<endl;
la=x+1;
}
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...