题意:给你一个序列,求满足要求的子序列个数,其中要求为:

1、子序列的max-子序列长度len<=k

2、子序列中不出现重复的数字

题解:首先看到子序列max,很容易想到枚举最大值然后分治,这个做法有人通过,但是我并没想到如何做

子序列max还有一个思路是单调队列,这里我们通过单调队列进行解题

首先对于给出的限制条件式子max-(r-l+1)<=k,我们进行移项,可得max+l<=k+r+1,此时我们将l和r分离至不等式两边

容易看出我们可以枚举右端点,然后维护一个权值线段树,每次只需要查询1~k+r+1区间的sum就可以了

以max+l作为权值建线段树

那么容易想到用单调队列进行维护max,每次更新单调队列的时候相当于在权值线段树上的一个区间进行+1/-1操作

单调队列维护:值,位置,这个值的左端点

维护单调队列时为了满足子序列不出现重复数字,于是考虑双向单调队列

新枚举右端点时,单调队列从右往左退栈,直到第一个不小于右端点数字的位置

然后考虑此时的最左边能到哪里

考虑记下每一个位置的前驱位置,即前一个相同数字在哪

容易想到维护一个最大值表示当前的最左端点在哪,每新枚举一个右端点,那么将last[i]+1和当前的左端点取个max,即为新的左端点,显然这样是当前右端点下最大的满足条件的左端点

于是单调队列从左往右退栈,直到第一个下标大于等于左端点的位置,然后再更新单调队列里维护的左端点

至此则可以在nlogn的时间内求出答案

时间复杂度O(nlogn)

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#define ll long long
using namespace std;
int T;
int n,k;
int a[],last[],hd[];
int lh[],mx[],mxi[],ln,rn;
ll ans;
class Segtree
{
public:
ll v[*],flv[*];
bool fl[*]; void init()
{
memset(v,,sizeof(v));
memset(fl,,sizeof(fl));
memset(flv,,sizeof(flv));
}
void pushdown(int l,int r,int pos)
{
if(fl[pos])
{
int mid=l+r>>;
v[pos<<]+=flv[pos]*(mid-l+);
v[pos<<|]+=flv[pos]*(r-mid);
flv[pos<<]+=flv[pos];
flv[pos<<|]+=flv[pos];
fl[pos<<]=fl[pos<<|]=;
fl[pos]=;flv[pos]=;
}
}
void pushup(int pos)
{
v[pos]=v[pos<<]+v[pos<<|];
}
void change(int l,int r,int al,int ar,ll tv,int pos)
{
if(l==al && r==ar)
{
v[pos]+=tv*(ar-al+);
fl[pos]=;
flv[pos]+=tv;
return;
}
pushdown(l,r,pos);
int mid=l+r>>;
if(ar<=mid)change(l,mid,al,ar,tv,pos<<);
if(al>mid)change(mid+,r,al,ar,tv,pos<<|);
if(al<=mid && ar>mid)
{
change(l,mid,al,mid,tv,pos<<);
change(mid+,r,mid+,ar,tv,pos<<|);
}
pushup(pos);
}
ll ask(int l,int r,int al,int ar,int pos)
{
if(l==al && r==ar)return v[pos];
int mid=l+r>>;
pushdown(l,r,pos);
if(ar<=mid)return ask(l,mid,al,ar,pos<<);
if(al>mid)return ask(mid+,r,al,ar,pos<<|);
if(al<=mid && ar>mid)return ask(l,mid,al,mid,pos<<)+ask(mid+,r,mid+,ar,pos<<|);
}
}segtree;
int main()
{
scanf("%d",&T);
while(T--)
{
segtree.init();
ans=;
memset(hd,,sizeof(hd));
memset(last,,sizeof(last));
scanf("%d%d",&n,&k);
for(int i=;i<=n;i++)
{
scanf("%d",&a[i]);
last[i]=hd[a[i]];
hd[a[i]]=i;
}
ln=;rn=;int tl,tl2=;
for(int i=;i<=n;i++)
{
tl=i;tl2=max(tl2,last[i]+);
while(ln<=rn && mx[rn]<a[i])
{
tl=min(tl,lh[rn]);
segtree.change(,n*,mx[rn]+lh[rn],mx[rn]+mxi[rn],-,);
rn--;
}
segtree.change(,n*,a[i]+tl,a[i]+i,,);
rn++;
lh[rn]=tl;mx[rn]=a[i];mxi[rn]=i;
while(ln<=rn && mxi[ln]<tl2)
{
segtree.change(,n*,mx[ln]+lh[ln],mx[ln]+mxi[ln],-,);
ln++;
}
if(ln<=rn && lh[ln]<tl2)
{
segtree.change(,n*,mx[ln]+lh[ln],mx[ln]+tl2-,-,);
lh[ln]=tl2;
}
ans+=segtree.ask(,n*,,min(i+k+,n*),);
}
printf("%lld\n",ans);
}
return ;
}

心得:区间max的处理方法不仅有枚举max然后分治,还有单调队列,思维不要唯一,要多想一些

最新文章

  1. .NET 基础 一步步 一幕幕[面向对象之方法、方法的重载、方法的重写、方法的递归]
  2. How to fix the conflict between ROS Python and Conda
  3. java设计模式--简单工厂模式
  4. September 12th 2016 Week 38th Monday
  5. struts拦截器实现原理
  6. silverlight 生成二维码
  7. Spring Security教程系列(一)基础篇-2
  8. Codeforces_499C:Crazy Town(计算几何)
  9. Python--socketserve源码分析(一)
  10. 动态增加表单元素并获取元素的text和value提交
  11. [代码]解析nodejs的require,吃豆人的故事
  12. Linux系统调用:进程的终止
  13. [转] KVM storage performance and cache settings on Red Hat Enterprise Linux 6.2
  14. centos7 centos中apache运行php需要连接mysql一直连不上,telnet访问mysql出错Connection closed by foreign host
  15. Qt532_QWebView做成DLL供VC/Delphi使用_Bug
  16. [转]Linux下彻底卸载mysql详解
  17. java 基础 --File
  18. 转:google测试分享-GTA
  19. Java程序运行在Docker等容器环境有哪些新问题
  20. PhantomJS + Selenium webdriver 总结-元素定位

热门文章

  1. STM32 I2C 难点---这个不错,留着慢慢研究
  2. php面向对象三大特性
  3. 使用 Vagrant 搭建 Kubernetes 本地测试环境
  4. MapReduce(2): How does Mapper work
  5. Git013--多人协作
  6. mysql 数据库表结构对比语句
  7. 利用Spring实现Hello World
  8. Codeforce 1175 D. Array Splitting
  9. Manacher(输出最长回文串及下标)
  10. [Java聊天室server]实战之三 接收循环