Solution P11880 [RMI 2024] 选区间 / Choose Interval
题意:对于每个区间可以选择这个区间内加一或区间外加一,要求最后最大值最小。
考虑二分答案。
对于一个答案 \(mid\),先假设所有操作均为区间内加,考虑钦定 \(k\) 个为区间外加。此时我们先对整体的值加 \(k\),就变成选 \(k\) 个区间内减 \(2\)。
如果确定了 \(mid,k\),可以扫描一遍整个序列,假设扫到 \(i\),如果值超出限制则找满足 \(l\le i\) 且 \(r\) 最大的区间 \([l,r]\)。用堆维护即可做到单次 check \(O(n\log n)\)。总体时间复杂度是 \(O(n^2\log^2n)\) 的。
考虑找出如果所有操作都为区间内加,此时序列最大的值 \(mx\) 和其位置 \(mxp\)。那么至少需要调整 \(k=mx-mid\) 个区间才能使得答案合法。我们大概有个思路:即 \(k\) 可能不会离 \(mx-mid\) 太远,因为这样子就可能在区间外产生新的最大值。
考虑证明。假设目前选择了 \(k\ (k\ge mx-mid+2)\) 个区间变成往外加,考虑能否调整为 \(k-2\) 个区间。
首先假设这 \(k\) 个区间中有两个不交,则将这两个区间改为内部加,发现序列中所有数的值都不降。因此可以调整。
否则所有区间交非空。发现 \(mxp\) 一定在所有区间的交内。原因是区间不交的情况优先调整,则最后剩下来的区间一定属于原来区间最多的那组(即 \(mxp\) 所在的位置)。考虑找出 \(mxp\) 左边最近的区间左边界对应区间 \(i\) 和右边最近的区间右边界对应区间 \(j\),则直接选择 \(i,j\) 改为内部加,此时影响(值加二)的范围一定最小且包含 \(mxp\)。由于 \(k\ge mx-mid+2\),因此此时 \(mxp\) 上的值一定小于等于 \(lim-2\)。而由于这个区间是最小的,因此这个区间内的所有值都相等(没有区间的边界在之内)。因此选择 \(i,j\) 的并加二仍然合法。
所以如果存在一个 \(k>mx-mid+2\) 合法,则 \(mx-mid\) 和 \(mx-mid+1\) 中至少有一个合法。check 一个 \(mid\) 时就只有 \(O(1)\) 个需要判断的 \(k\)。时间复杂度变为 \(O(n\log^2n)\)。
代码
const int N=400005;
int n;
pii a[N];
int s[N];
priority_queue<pii>q;
vector<int>id[N];
bool rev[N];
il bool chk(int lim,int cnt){
lim-=cnt;
while(!q.empty())q.pop();
forto(i,0,n*2+1)s[i]=0;
forto(i,1,n)s[a[i].first]++,s[a[i].second+1]--,rev[i]=0;
forto(i,1,n*2+1){
for(int x:id[i])q.push(mkp(a[x].second,x));
s[i]+=s[i-1];
while(s[i]>lim){
if(q.empty()||cnt<=0||q.top().first<i)return 0;
s[i]-=2,s[q.top().first+1]+=2,rev[q.top().second]=1,q.pop(),cnt--;
}
}
return 1;
}
int mx;
signed main(){
n=read();
int l,r,mid,ans;
forto(i,1,n)l=read(),r=read(),a[i]=mkp(l,r),id[l].eb(i),s[l]++,s[r+1]--;
forto(i,1,n*2+1)s[i]+=s[i-1],mx=max(mx,s[i]);
l=0,r=mx,ans=-1;
while(l<=r){
mid=(l+r)>>1;
if(chk(mid,mx-mid)||chk(mid,mx-mid+1))ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d\n",ans);
if(!chk(ans,mx-ans))chk(ans,mx-ans+1);
forto(i,1,n)printf("%d",!rev[i]);
printf("\n");
return 0;
}