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

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然后分治,还有单调队列,思维不要唯一,要多想一些

【HDU6701】Make Rounddog Happy【权值线段树+双向单调队列】的更多相关文章

  1. 【树状数组套权值线段树】bzoj1901 Zju2112 Dynamic Rankings

    谁再管这玩意叫树状数组套主席树我跟谁急 明明就是树状数组的每个结点维护一棵动态开结点的权值线段树而已 好吧,其实只有一个指针,指向该结点的权值线段树的当前结点 每次查询之前,要让指针指向根结点 不同结 ...

  2. 【BZOJ-2892&1171】强袭作战&大sz的游戏 权值线段树+单调队列+标记永久化+DP

    2892: 强袭作战 Time Limit: 50 Sec  Memory Limit: 512 MBSubmit: 45  Solved: 30[Submit][Status][Discuss] D ...

  3. BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)

    题目大意:有一些位置.这些位置上能够放若干个数字. 如今有两种操作. 1.在区间l到r上加入一个数字x 2.求出l到r上的第k大的数字是什么 思路:这样的题一看就是树套树,关键是怎么套,怎么写.(话说 ...

  4. 动态求区间K大值(权值线段树)

    我们知道我们可以通过主席树来维护静态区间第K大值.我们又知道主席树满足可加性,所以我们可以用树状数组来维护主席树,树状数组的每一个节点都可以开一颗主席树,然后一起做. 我们注意到树状数组的每一棵树都和 ...

  5. 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题

    “队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄>     线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...

  6. 【BZOJ3685】【zkw权值线段树】普通van Emde Boas树

    原题传送门 因为马上要开始搞树套树了,所以学了一波权值线段树...毕竟是会点zkw线段树的,所以zkw线段树大法好! 解题思路: 介绍一下权值线段树吧,其实感觉就是线段树的本义,就是你用线段树维护了数 ...

  7. BZOJ_2161_布娃娃_权值线段树

    BZOJ_2161_布娃娃_权值线段树 Description 小时候的雨荨非常听话,是父母眼中的好孩子.在学校是老师的左右手,同学的好榜样.后来她成为艾利斯顿第二 代考神,这和小时候培养的良好素质是 ...

  8. BZOJ_3685_普通van Emde Boas树_权值线段树

    BZOJ_3685_普通van Emde Boas树_权值线段树 Description 设计数据结构支持: 1 x  若x不存在,插入x 2 x  若x存在,删除x 3    输出当前最小值,若不存 ...

  9. B20J_2733_[HNOI2012]永无乡_权值线段树合并

    B20J_2733_[HNOI2012]永无乡_权值线段树合并 Description:n座岛,编号从1到n,每座岛都有自己的独一无二的重要度,按照重要度可以将这n座岛排名,名次用1到 n来表示.某些 ...

随机推荐

  1. Embedding理解与代码实现

    https://blog.csdn.net/songyunli1111/article/details/85100616

  2. 拒绝从入门到放弃_《鸟哥的 Linux 私房菜 — 基础学习篇(第三版)》必读目录

    目录 目录 前言 关于这本书 必看知识点 最后 前言 相信部分刚进入这个行业的新同学会对一个问题感到疑惑,为什么从培训学校出来的学员不被欢迎? 这里记录下一些我个人的看法(博主也曾有面试新员工的经历) ...

  3. Openstack_SQLAlchemy 修改数据库的表结构

    目录 目录 前言 更改数据库的方法 为数据库添加一张或多张新表 删除一张或多张表 为旧表添加一个字段 为旧表更新一个字段 为旧表初始化一条新的记录 最后 前言 SQLAlchemy 的使用方法和相关基 ...

  4. git查看某个文件的提交历史

    1. git log --pretty=oneline 文件名 文件名是文件路径+文件名,输入完整 输入正确后,打印出版本号的列表 2. git show <git提交版本号> <文 ...

  5. MAC-安装套件管理工具Homebrew

    前言 Homebrew是一款Mac OS下的套件管理工具,拥有安装.卸载.更新.查看.搜索等很多实用的功能. Homebrew安装 1,Homebrew官网获取安装指令,官网地址:https://br ...

  6. JSP基础--三大指令

    JSP指令 1       JSP指令概述 JSP指令的格式:<%@指令名 attr1=”” attr2=”” %>,一般都会把JSP指令放到JSP文件的最上方,但这不是必须的. JSP中 ...

  7. Requests爬取网页的编码问题

    Requests爬取网页的编码问题 import requests from requests import exceptions def getHtml(): try: r=requests.get ...

  8. spring boot 控制 序列化格式

    @PostMapping(path = "/register", produces={MediaType.APPLICATION_JSON_VALUE, MediaType.TEX ...

  9. ToString()的简单介绍

    1.在某一个类中重写该类的toString()方法,是为了方便打印该类实例中的内容.

  10. Python入门习题6.猜数游戏和其异常处理

    例6:在程序中预设一个0~9之间的整数,让用户通过键盘输入所猜的数,如果大于预设的数,显示“遗憾,太大了”:小于预设的数,显示“遗憾,太小了”,如此循环,直至猜中数,显示”预测N次,猜中了”,其中N是 ...