Loading

CF1712D 题解

\(\to \text{CF1712D Empty Graph}\leftarrow\)

思路:

赛时想到了一个 \(O(n\log n\log A)\) (\(A\) 表示值域)的解法。

先考虑 \(x\to y\ (x<y)\) 的最短距离 \(dis_{x,y}\) 怎么更新。

( 我们假定 \(mn_{l,r}\) 表示 \(\min\{a_l,a_{l+1}\cdots a_r\}\) )

显然 \(dis_{x,y}\) 由三部分取最小值组成:

  • \(x\) 直接到 \(y\),这部分的距离是 \(mn_{x,y}\)

  • \(x\) 先到 \(z\) 再到 \(y\),且满足 \(z<x\),这部分的距离是 \(mn_{z,x}+mn_{z,y}\)

  • \(x\) 先到 \(z\) 再到 \(y\),且满足 \(z>y\),这部分的距离是 \(mn_{x,z}+mn_{y,z}\)

显然,第二部分当 \(z=1\) 时有最小值 \(mn_{1,x}+mn_{1,y}\),第三部分当 \(z=n\) 时有最小值 \(mn_{x,n}+mn_{y,n}\)

然后我发现答案满足单调性,于是二分答案 \(mid\),并且修改 \(a_{x\sim y}\) 中值小于 \(mid\) 的,这样对于第二部分和第三部分,由于 \(mn_{x,y}\ge mid\),实际上我们只用使 \(2\cdot mn_{1,x-1}\ge mid\)\(2\cdot mn_{y+1,n}\ge mid\)

也就是我们需要修改 \(1\le i\le x-1\)\(y+1\le i\le n\)\(a_i\le \lceil \frac{mid}{2} \rceil\) 中的 \(a_i\),然后判断总修改次数是否大于 \(k\) 次即可。

我们可以维护一棵主席树来计算需要修改的数,这样做的时间复杂度是 \(O(n^2\log n\log A)\) 的,显然需要一些优化。

然后我大胆猜测对于每一个数 \(y\) 能使得修改次数最小的 \(x\) 必然是 \(y-1\),赛时交了一发,然后就过了。

赛后证明一下:

如果选 \(x,y\) 的话,必然需要使得 \(a_{1\sim x-1,y+1\sim n}\ge \lceil \frac{mid}{2} \rceil\)\(a_{x\sim y}\ge mid\)

\(x=y-1\) 的话那么就可以让 \(a_{x\sim y}\ge mid\)\(a_i\) 尽量少,这样限制条件就更少 (只用 \(\ge \lceil \frac{mid}{2} \rceil\)),也就使得修改的次数更少,也就说明 \(x=y-1\) 时最优,证毕。

这样的时间复杂度是 \(O(n\log n\log A)\) 的,而数据范围是 \(1\le n\le 10^5\) ,故能够通过此题。

code

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
const int inf=1e9;

int n,k;
int a[N],rt[N];

namespace CT{
	struct node{
		int v,l,r;
	}t[N*31];
	int cnt;
	inline void insert(int &p,int pre,int l,int r,int x,int k){
		p=++cnt;
		t[p]=t[pre];
		++t[p].v;
		if(l==r) return;
		int mid=l+r>>1;
		if(x<=mid) insert(t[p].l,t[pre].l,l,mid,x,k);
		else insert(t[p].r,t[pre].r,mid+1,r,x,k);
	}
	inline int query(int q,int p,int l,int r,int L,int R){
		if(L<=l&&r<=R) return t[p].v-t[q].v;
		int mid=l+r>>1,res=0;
		if(L<=mid) res+=query(t[q].l,t[p].l,l,mid,L,R);
		if(mid<R) res+=query(t[q].r,t[p].r,mid+1,r,L,R);
		return res;
	}
}

inline bool check(int x,int i){
    int now=0;
    if(a[i]<x) ++now;
    if(a[i-1]<x) ++now;
    int num=x/2+(x%2);
    if(now>k) return 0;
    if(num==1) return 1;
    now+=CT::query(rt[0],rt[i-2],1,inf,1,num-1);
    now+=CT::query(rt[i],rt[n],1,inf,1,num-1);
    //cout<<x<<" "<<i<<" "<<num<<" "<<now<<endl;
    return now<=k;
}

signed main(){
	int T;
	cin>>T;
	while(T--){
		cin>>n>>k;
		int ans=0;
        rt[0]=0;
		for(int i=1;i<=n;++i){
			cin>>a[i];
			CT::insert(rt[i],rt[i-1],1,inf,a[i],1);
		}
		for(int i=2;i<=n;++i){
			int l=1,r=1e9+1;
			while(l<r-1){
				int mid=l+r>>1;
				if(check(mid,i)) l=mid;
				else r=mid;
			}
			ans=max(ans,l);
		}
		cout<<ans<<endl;
	}
}

当然还有时间复杂度更加优秀的做法,具体可以去看官方题解。

posted @ 2022-08-14 20:04  Into_qwq  阅读(45)  评论(0编辑  收藏  举报