CF407E k-d-sequence
阿新 • • 發佈:2021-07-14
一、題目
二、解法
注意題目問的是區間啊,我以為是子序列就一直做不起。
\(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); }