CF407E k-d-sequence

时间:2021-07-14
本文章向大家介绍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);
}

原文地址:https://www.cnblogs.com/C202044zxy/p/15010229.html