1. 程式人生 > 其它 >Codeforces 407E - k-d-sequence(單調棧+掃描線+線段樹)

Codeforces 407E - k-d-sequence(單調棧+掃描線+線段樹)

單調棧+掃描線+線段樹,套路題

Codeforces 題面傳送門 & 洛谷題面傳送門

深感自己線段樹學得不紮實……

首先特判掉 \(d=0\) 的情況,顯然這種情況下滿足條件的區間 \([l,r]\) 中的數必須相同,雙針掃一遍即可。

接下來考慮如何解決 \(d\ne 0\) 的情況。碰到這樣的問題我們肯定首先要把區間合法的充要條件一一羅列出來,不難發現由於我們的過程只有加數,沒有刪數,因此原序列中兩兩數之差也必須是 \(d\) 的倍數,也即區間中所有數模 \(d\) 同餘,又顯然區間中兩兩數必須互不相同,因此我們考慮令 \(b_i=\lfloor\dfrac{a_i}{d}\rfloor,c_i=a_i\bmod d\)

,那麼前面兩個條件即可翻譯為:

  • \(\forall i\in[l,r],c_i=c_l\)
  • \(b_l,b_{l+1},b_{l+2},\cdots,b_r\) 互不相同

接下來考慮最多加入 \(k\) 個數這個條件。顯然經過我們這麼一轉化,最終形成的序列的 \(b\) 值必須形成公差恰好為 \(1\) 的等差數列。而如果我們記 \(L=\min\limits_{i=l}^rb_i,R=\max\limits_{i=l}^rb_i\),那麼我們肯定不會加入 \(b\) 值在 \([L,R]\) 以外的數,因此我們加入數的個數的最小值就是 \((R-L+1)-(r-l+1)=(R-L)-(r-l)\)

,因此我們還可以得到條件:

  • \((R-L)-(r-l)\le k\)

考慮怎麼維護這個東西,這東西顯然不好分治對吧,那我們就掃描線求解,列舉右端點,維護可行的左端點的集合。假設右端點掃描到 \(r\),那麼顯然滿足前兩個條件的 \(l\) 肯定會形成一段區間 \([L_l,R_l]\),且顯然有 \(R_l=r\)。那麼對於第一個條件,如果我們掃描到某個 \(r\) 滿足 \(c_r\ne c_{r-1}\),就令 \(L_l=r\),對於第二個條件,我們在掃描的過程中維護 \(pre_i\) 表示上一個 \(b_j=i\) 的位置,然後每掃到一個 \(r\) 就令 \(L_l\)\(pre_{b_r}+1\)

\(\max\) 即可。比較棘手的是第三個條件,不過這東西是可以單調棧+線段樹維護的,具體維護方法參加 CF997E,因此考慮單調棧維護一波這個東西,這樣我們要求的就是 \([L_l,R_l]\) 中第一個 \(\le k\) 的位置,這顯然可以線段樹二分在 \(\log n\) 的時間內求出(而我甚至 \(\log^2n\) 的做法都沒想到,說明 5448),總複雜度 \(n\log n\)

const int MAXN=2e5;
int n,k,d,a[MAXN+5],b[MAXN+5],c[MAXN+5];
map<int,int> pre;
int getmod(int x,int v){return (x%v+v)%v;}
int getdiv(int x,int v){return (x-getmod(x,v))/v;}
struct node{int l,r,mn,lz;} s[MAXN*4+5];
void pushup(int k){s[k].mn=min(s[k<<1].mn,s[k<<1|1].mn);}
void build(int k,int l,int r){
	s[k].l=l;s[k].r=r;if(l==r) return;int mid=l+r>>1;
	build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void tag(int x,int v){s[x].mn+=v;s[x].lz+=v;}
void pushdown(int k){if(s[k].lz) tag(k<<1,s[k].lz),tag(k<<1|1,s[k].lz),s[k].lz=0;}
void modify(int k,int l,int r,int v){
	if(l<=s[k].l&&s[k].r<=r) return tag(k,v),void();
	pushdown(k);int mid=s[k].l+s[k].r>>1;
	if(r<=mid) modify(k<<1,l,r,v);else if(l>mid) modify(k<<1|1,l,r,v);
	else modify(k<<1,l,mid,v),modify(k<<1|1,mid+1,r,v);
	pushup(k);
}
int findleq(int k,int l,int r,int v){
	if(s[k].mn>v) return -1;
	if(l<=s[k].l&&s[k].r<=r){
		if(s[k].l==s[k].r) return s[k].l;
		pushdown(k);int ps,mid=s[k].l+s[k].r>>1;
		if(~(ps=findleq(k<<1,l,mid,v))) return ps;
		return findleq(k<<1|1,mid+1,r,v);
	} pushdown(k);int mid=s[k].l+s[k].r>>1;
	if(r<=mid) return findleq(k<<1,l,r,v);
	else if(l>mid) return findleq(k<<1|1,l,r,v);
	else{
		int ps;
		if(~(ps=findleq(k<<1,l,mid,v))) return ps;
		return findleq(k<<1|1,mid+1,r,v);
	}
}
int main(){
	scanf("%d%d%d",&n,&k,&d);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	if(!d){
		pii res=mp(0,0);
		for(int l=1,r;l<=n;l++){
			r=l;while(r<=n&&a[l]==a[r]) ++r;
			chkmax(res,mp(r-l,-l));
		} printf("%d %d\n",-res.se,-res.se+res.fi-1);
		return 0;
	} build(1,1,n);pii res;
	for(int i=1;i<=n;i++) b[i]=getdiv(a[i],d),c[i]=getmod(a[i],d);
	stack<int> stk_mn,stk_mx;stk_mn.push(0);stk_mx.push(0);
	for(int i=1,mnl=1;i<=n;i++){//(mx-mn)-(r-l)<=k
		if(c[i]!=c[i-1]) chkmax(mnl,i);
		chkmax(mnl,pre[b[i]]+1);pre[b[i]]=i;
		while(stk_mn.size()>1&&b[i]<b[stk_mn.top()]){
			int p=stk_mn.top();stk_mn.pop();
			modify(1,stk_mn.top()+1,p,b[p]-b[i]);
//			printf("modify %d %d %d\n",stk_mn.top()+1,p,b[p]-b[i]);
		} while(stk_mx.size()>1&&b[i]>b[stk_mx.top()]){
			int p=stk_mx.top();stk_mx.pop();
			modify(1,stk_mx.top()+1,p,-b[p]+b[i]);
//			printf("modify %d %d %d\n",stk_mx.top()+1,p,-b[p]+b[i]);
		} if(i^1) modify(1,1,i-1,-1)/*,printf("modify %d %d %d\n",1,i-1,-1)*/;
		stk_mn.push(i);stk_mx.push(i);
		int ps=findleq(1,mnl,i,k);
		if(~ps) chkmax(res,mp(i-ps+1,-ps));
	} printf("%d %d\n",-res.se,-res.se+res.fi-1);
	return 0;
}