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);
}