【洛谷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;//输出答案
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5070.html