传送门
文章目录
- 目录:
- 题意:
- 思路:
目录:
题意:
给你一个长度为nnn的数组aaa以及kkk,让你选择一个值域[x,y][x,y][x,y],满足能将该数组分成连续的kkk段并且每段中值域在[x,y][x,y][x,y]内的个数严格大于不在其范围内的数的个数,要求你给出[x,y][x,y][x,y]并且让y−xy-xy−x最小,让后给出分段的方案。
1≤k≤n≤2e5,1≤ai≤n1\le k\le n\le 2e5,1\le a_i\le n1≤k≤n≤2e5,1≤ai≤n
思路:
一开始想错了,二分了xxx和yyy还感觉很对。。。
可以发现[x,y][x,y][x,y]这是一个连续的区间,我们可以利用这个进行尺取,现在问题就变成了如何检验给定[x,y][x,y][x,y]是否合法。
我们将值域在[x,y][x,y][x,y]内的数看成111,不在值域内的数看成−1-1−1,设新数组preprepre就是将aaa换成111和−1-1−1后的前缀和数组,那么一个子区间合法的条件就是[l,r][l,r][l,r]满足pre[r]−pre[l−1]>0pre[r]-pre[l-1]>0pre[r]−pre[l−1]>0,顺着这个思路,我们不难发现分段方案就是在preprepre数组中找一个从0开始的上升序列,长度为k+1k+1k+1,那么最终pre[n]pre[n]pre[n]一定需要满足pre[n]>=kpre[n]>=kpre[n]>=k,此时就比较好搞了,我们先尺取求出来[x,y][x,y][x,y],判断条件就是pre[n]>=kpre[n]>=kpre[n]>=k,pre[n]pre[n]pre[n]是所有数的和,只需要存一下当前数有几个即可,最后输出方案。
复杂度O(n)O(n)O(n)
#include<bits/stdc++.h>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define Mid (tr[u].l+tr[u].r>>1)
#define pb push_back
using namespace std;const int N=1000010,INF=0x3f3f3f3f,mod=1e9+7;
typedef long long LL;int n,k;
int a[N],cnt[N];void solve() {scanf("%d%d",&n,&k);for(int i=1;i<=n;i++) scanf("%d",&a[i]),cnt[a[i]]++;int x,y,sum=-n;x=0; y=INF;for(int l=1,r=0;r<=n;) {while(r+1<=n&&sum<k) sum+=cnt[++r]*2;if(sum<k) break;if(r-l+1<y-x+1) x=l,y=r;sum-=cnt[l++]*2;}printf("%d %d\n",x,y);sum=0;int cntt=1;int l=1;for(int i=1;i<=n;i++) {if(a[i]>=x&&a[i]<=y) sum++;else sum--;if(sum==cntt) {cntt++;if(cntt==k+1) {printf("%d %d\n",l,n);break;}else printf("%d %d\n",l,i),l=i+1;}}for(int i=1;i<=n;i++) cnt[a[i]]=0;
}int main() {int _; scanf("%d",&_);while(_--) {solve();}}