• 【洛谷5070】[Ynoi2015] 即便看不到未来(树状数组)


    点此看题面

    大致题意: 给你一个序列,每次询问给出一个区间。将这一区间中的元素排序去重后(不影响原序列),分别求出长度为(1sim10)的极长值域连续段个数(值域连续要求从小到大,极长指该段的左右元素加入后都不能满足其连续性)。

    前言

    刷题刷得好累,于是昨晚翻了好久的Ynoi,最后找到这题来愉悦一下。

    一开始想到莫队,然后发现数据范围(1e6)。。。

    最后总算有点思路,结果一个晚上都没做出来。。。

    今天还是看了看题解,才发现自己原先的方法太复杂了。

    改了改后一交(WA)了最后两个点,没办法只能拿题解对拍。上个厕所回来发现(RE)了,最终发现可能是因为数据越界才导致(WA)了。

    调了调边界总算过了。

    大致想法

    我们将询问按右端点排序,这样就相当于不断在序列右边加数然后维护每个左端点的答案。

    考虑新加入的一个数(a_i)加入对原先答案的贡献:作为单独一段、使某个序列变长、连接两个序列、没有任何贡献(原先的一段中已经有与之相同的数)。

    然后我们发现对于能够产生贡献的三种情况,其实可以归为一谈。(而那个没有贡献的还有必要考虑吗。。。)

    我们设(p,q)分别表示在当前左端点已经存在((a_i-p)sim (a_i-1))((a_i+1)sim (a_i+q)),那么,我们就需要删去原本的这两段(因为它们已不再是极长),然后加入一段((a_i-p)sim(a_i+q))

    这个修改相当于是对一段区间内的答案都造成了影响,又由于只要求长度为(1sim10)的答案,因此可以考虑用(10)个树状数组来分别维护每种长度的答案。

    同理,正是因为只需要(1sim10)的答案,我们只需要求出所有出现在(lst_{a_i})右侧的在((a_i-11)sim(a_i+11))范围内的数,然后按位置从大到小依次处理,同时维护(p,q),就可以实现对答案的维护了。

    具体实现可以详见代码。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 1000000
    using namespace std;
    int n,m,a[N+5],lst[N+5];char ans[N+5][11];
    struct Qry
    {
    	int id,l,r;I Qry(CI p=0,CI a=0,CI b=0):id(p),l(a),r(b){}
    	I bool operator < (Con Qry& o) Con {return r<o.r;}
    }Q[N+5];
    struct data
    {
    	int x,y;I data(CI a=0,CI b=0):x(a),y(b){}
    	I bool operator < (Con data& o) Con {return x>o.x;}
    }P[40];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define tn (x<<3)+(x<<1)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
    }F;
    class TreeArray//树状数组(这里是维护后缀和)
    {
    	private:
    		int a[N+5];
    	public:
    		I void U(RI x,CI v) {W(x) a[x]+=v,x-=x&-x;}
    		I int Q(RI x,RI t=0) {W(x<=n) t+=a[x],x+=x&-x;return t;}
    }T[11];
    int main()
    {
    	RI i,j;for(F.read(n),F.read(m),i=1;i<=n;++i) F.read(a[i]);
    	for(i=1;i<=m;++i) Q[i].id=i,F.read(Q[i].l),F.read(Q[i].r);sort(Q+1,Q+m+1);//给询问排序
    	RI p,q,t,k=1;for(i=1;i<=n;++i)
    	{
    		for(p=q=t=0,j=max(a[i]-11,1);j<=min(N,a[i]+11);++j) lst[j]>lst[a[i]]&&(P[++t]=data(lst[j],j),0);//只需处理a[i]-11~a[i]+11范围内的数
    		for(P[++t]=data(i,a[i]),P[t+1]=data(lst[a[i]]),sort(P+1,P+t+1),j=1;j<=t;++j)
    		{
    			if(P[j].y<a[i]) W(p<=10&&(a[i]-p-1==P[j].y||lst[a[i]-p-1]>P[j].x)) ++p;//维护p
    			if(P[j].y>a[i]) W(q<=10&&(a[i]+q+1==P[j].y||lst[a[i]+q+1]>P[j].x)) ++q;//维护q
    			p>=1&&p<=10&&(T[p].U(P[j].x,-1),T[p].U(P[j+1].x,1),0),//删去非极长的一段
    			q>=1&&q<=10&&(T[q].U(P[j].x,-1),T[q].U(P[j+1].x,1),0),//删去非极长的一段
    			p+q+1<=10&&(T[p+q+1].U(P[j].x,1),T[p+q+1].U(P[j+1].x,-1),0);//加上新的一段
    		}lst[a[i]]=i;//更新这个数上一次出现的位置为当前位置
    		W(k<=m&&Q[k].r==i) {for(j=1;j<=10;++j) ans[Q[k].id][j]=T[j].Q(Q[k].l)%10+48;++k;}//处理询问
    	}
    	for(i=1;i<=m;++i) puts(ans[i]+1);return 0;//输出答案
    }
    
  • 相关阅读:
    cmake 学习记录
    OCRTesseract
    IEquatable<T>、IEnumerable<T>
    基于jackson 注解 入参 枚举 反序列化 实操
    基于jackson 将一个String 类型的Json字符串对外输出成 json 格式。
    mysql 中sql 语句查询今天、昨天、7天、近30天、本月、上一月 数据
    mysql 计算两个日期的天数
    iis上json解析失败404
    mybatis foreach split
    mybatis中大于等于小于等于的写法
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5070.html
Copyright © 2020-2023  润新知