1. 程式人生 > 其它 >CF407E k-d-sequence

CF407E k-d-sequence

一、題目

點此看題

二、解法

注意題目問的是區間啊,我以為是子序列就一直做不起。

\(d=0\) 特判,然後我們只考慮連續的\(\bmod d\) 餘數相同的一段,現在翻譯一下題目條件:

  • \([l,r]\) 中不出現相同的數。
  • \(\frac{\max[l:r]-\min[l:r]}{d}\leq k+r-l\),移個項:\(\max[l:r]-\min[l:r]+l\cdot d\leq (k+r)\cdot d\)

主要問題是第二個限制,我們把左邊的叫做區間 \([l,r]\) 的權值。那麼我們固定右端點,維護出每個左端點的權值,想到用線段樹維護權值,但是 \(\max,\min\) 有點不好搞。根據套路可以維護一個單調棧,棧內的每個元素都有一個管轄區間,代表如果 \(l\)

取值再這個區間內,那麼最值就是這個元素。我們在彈出棧頂時線上段樹上區間修改這個棧頂管轄區間的貢獻即可。

然後線上段樹上二分查權值不超過某個值得最小左端點即可。最後說下怎麼做第一個限制,你會發現滿足第一個限制的 \(l\) 是單調的,可以 \(\tt two-pointers\),發現不行了直接永久刪除即可(打極大值標記)

時間複雜度 \(O(n\log n)\)

三、總結

區間問題考慮左端點用來幹什麼?右端點用來幹什麼?怎麼維護?

最大最小值可以單調棧來輔助線段樹維護,這個套路以前優化 \(dp\) 時也見過。

#include <cstdio>
#include <iostream>
#include <map>
using namespace std;
const int M = 200005;
#define int long long
const int inf = 1e18;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,d,ans,pl,pr,a[M],b[M];
int s1[M],s2[M],mi[4*M],tag[4*M];
void down(int i)
{
	if(!tag[i]) return ;
	tag[i<<1]+=tag[i];
	tag[i<<1|1]+=tag[i];
	mi[i<<1]+=tag[i];
	mi[i<<1|1]+=tag[i];
	tag[i]=0;
}
void ins(int i,int l,int r,int L,int R,int v)
{
	if(L>r || l>R) return ;
	if(L<=l && r<=R)
	{
		mi[i]+=v,tag[i]+=v;
		return ;
	}
	int mid=(l+r)>>1;down(i);
	ins(i<<1,l,mid,L,R,v);
	ins(i<<1|1,mid+1,r,L,R,v);
	mi[i]=min(mi[i<<1],mi[i<<1|1]);
}
int find(int i,int l,int r,int v)
{
	if(mi[i]>v) return 0;
	if(l==r) return l;
	int mid=(l+r)>>1;down(i);
	if(mi[i<<1]<=v) return find(i<<1,l,mid,v);
	return find(i<<1|1,mid+1,r,v);
}
void work(int x)
{
	for(int i=1;i<=4*m;i++)
		mi[i]=tag[i]=0;
	int t1=0,t2=0,L=0;
	ins(1,1,m,1,m,inf);
	map<int,int> cnt;
	for(int i=1;i<=m;i++)
	{
		ins(1,1,m,i,i,-inf+i*d);//light it up
		//maintain MAX
		while(t1 && b[s1[t1]]<=b[i])
		{
			ins(1,1,m,s1[t1-1]+1,s1[t1],-b[s1[t1]]);
			t1--;
		}
		ins(1,1,m,s1[t1]+1,i,b[i]);
		s1[++t1]=i;
		//maintain MIN
		while(t2 && b[s2[t2]]>=b[i])
		{
			ins(1,1,m,s2[t2-1]+1,s2[t2],b[s2[t2]]);
			t2--;
		}
		ins(1,1,m,s2[t2]+1,i,-b[i]);
		s2[++t2]=i;
		//a[l:r] can't have same value
		cnt[b[i]]++;
		while(cnt[b[i]]>1)
		{
			L++;
			ins(1,1,m,L,L,inf);//turn it down
			cnt[b[L]]--;
		}
		//updata the answer
		int t=find(1,1,m,(i+k)*d);//all d times
		if(t && ans<i-t+1)
			ans=i-t+1,pl=t+x-1,pr=i+x-1;
	}
}
signed main()
{
	n=read();k=read();d=read();
	for(int i=1;i<=n;i++) a[i]=read(); 
	if(d==0)
	{
		for(int i=1,j=i;i<=n;)
		{
			while(j<=n && a[i]==a[j]) j++;
			if(j-i>ans)
				ans=j-i,pl=i,pr=j-1;
			i=j;
		}
		printf("%lld %lld\n",pl,pr);
		return 0;
	}
	for(int i=1,j=i;i<=n;)
	{
		m=0;
		while(j<=n && (a[i]%d+d)%d==(a[j]%d+d)%d)
			b[++m]=a[j],j++;
		work(i);i=j;
	}
	printf("%lld %lld\n",pl,pr);
}