「HNOI2016」序列

Posted Kan_kiz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「HNOI2016」序列相关的知识,希望对你有一定的参考价值。

[HNOI2016]序列 草稿纸

P3246 [HNOI2016]序列
给定一个长度为n的序列,q个询问,每次询问[l,r]的所有子段的最小值之和。

设f[i]为以i为右端点的区间答案,pre[i]为i左边第一个比a[i]小的数的位置。 可以发现如果这个左端点<=pre[i],其实右端点放在pre[i]和放在i答案是一样的。
所以有\\(f[i]=a[i]*(i-pre[i])+f[pre[i]]\\)
\\(=>f[i]-f[pre[i]]=a[i]*(i-pre[i])\\)(其实是左端点在pre[i]+1~i之间,右端点在i的答案)
又或者\\(f[i]=a[i]*(i-pre[i])+a[pre[i]]*(pre[i]-pre[pre[i]])+f[pre[pre[i]]]\\)
\\(=>f[i]-f[pre[pre[i]]]=(f[i]-f[pre[i]])+(f[pre[i]]-f[pre[pre[i]]])=a[i]*(i-pre[i])+a[pre[i]]*(pre[i]-pre[pre[i]])\\)(其实是左端点在pre[pre[i]]+1~i之间,右端点在i的答案)(这样一定可以处理左端点为i左边一个比i小的数到i这个区间的答案)

然后就考虑左端点在\\([l,r]\\),右端点在\\(r+1\\)的答案....
那么其实可以p=r+1; while (pre[p]>=l) p=pre[p]; 其实发现这样最后得到的p就是[l,r+1]的最小值位置,用RMQ可以O1或Ologn求出
r+1的时候答案的增量计算分为两部分
对于左端点在\\([p+1,r+1]\\),右端点在\\(r+1\\)的部分,答案就为\\(f[r+1]-f[p]\\)
对于左端点在\\([l,p]\\),右端点在\\(r+1\\)的部分,答案就为\\(a[p]*(p-l+1)\\)
这样可以离线询问Onsqrtn莫队完成

在线怎么写(
直接考虑一个区间\\([l,r]\\)的答案
w...怎么搞呢
考虑右端点在r的答案...同样找出一个最小值位置p。
那么左端点在\\([p+1,r]\\)的答案是\\(f[r]-f[p]\\)

右端点在\\(r-1\\),左端点在\\([p+1,r-1]\\)的答案是\\(f[r-1]-f[p]\\)
那左端点在\\([p+1,右端点]\\),右端点在\\([p+1,r]\\),也即\\([p+1,r]\\)区间内的答案为 \\(( \\Sigma_{i = p+1}^{r} f[i] ) - f[p]*(r-p)\\)
对f做个前缀和数组c 答案就是 \\(c[r]-c[p]-f[p]*(r-p)\\)

\\([l,p-1]\\)区间内的答案,可以倒过来求的样子(
答案是 \\(c[p-1]-c[l-1]-f[p]*(p-l)\\)

覆盖p的区间答案,\\(a[p]*(p-l+1)*(r-p+1)\\)

QAQ 完结....居然写了这么久思路才理清楚

code:
一遍过样例,爆long long 喜提0pts,暴躁kkz在线#define int long long

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
#define MAXN (int)(1e5+233)
#define MAXA (int)(1e9+233)
#include <stack>
struct qwq
{
	long long a,id;
};
stack<qwq> st;
#define int long long
int a[MAXN];
int n,q;

int prer[MAXN],prel[MAXN];



inline void mostack()
{
	for (int i=1;i<=n;i++)
	{
		while (!st.empty()&&st.top().a>a[i])
		{
			prer[st.top().id]=i;
			st.pop();
		}
		st.push((qwq){a[i],i});
	}
	while (!st.empty()) st.pop();
	for (int i=n;i;i--)
	{
		while (!st.empty()&&st.top().a>a[i])
		{
			prel[st.top().id]=i;
			st.pop();
		}
		st.push((qwq){a[i],i});
	}
	return;
}
int ans[MAXN<<2];
#define leftson cur<<1
#define rightson cur<<1|1
#define mid ((l+r)>>1)
#define push_up if (a[ans[leftson]]<a[ans[rightson]]) ans[cur]=ans[leftson]; else ans[cur]=ans[rightson]
void build(int cur,int l,int r)
{
	if (l==r)
	{
		ans[cur]=l;
		return;
	}
	build(leftson,l,mid);
	build(rightson,mid+1,r);
	push_up;
}
int query(int ql,int qr,int cur,int l,int r)
{
	if (ql<=l&&r<=qr) return ans[cur];
	int answer=0;
	if (ql<=mid) answer=query(ql,qr,leftson,l,mid);
	if (qr>mid) { int tt=query(ql,qr,rightson,mid+1,r); if (!answer) answer=tt; else answer=(a[tt]>a[answer]?answer:tt); }
	return answer;
}

inline void RMQINIT() { build(1,1,n); return; }
long long fl[MAXN],cl[MAXN],fr[MAXN],cr[MAXN];
inline void FINIT()
{
	for (int i=1;i<=n;i++)
		fl[i]=a[i]*(i-prel[i])+fl[prel[i]];
	for (int i=n;i;i--)
		fr[i]=a[i]*(prer[i]-i)+fr[prer[i]];
	for (int i=1;i<=n;i++)
		cl[i]=cl[i-1]+fl[i];
	for (int i=n;i;i--)
		cr[i]=cr[i+1]+fr[i];
	return;
}

signed main()
{
	scanf("%lld%lld",&n,&q);
	for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
	mostack();
	RMQINIT();
	FINIT();
	int l,r,p;
	long long ans=0;
	while (q--)
	{
		scanf("%lld%lld",&l,&r);
		p=query(l,r,1,1,n);
		ans=0;
		if (p+1<=r) ans=cl[r]-cl[p]-fl[p]*(r-p);
		if (p-1>=l) ans+=cr[l]-cr[p]-fr[p]*(p-l);
		ans+=a[p]*(p-l+1)*(r-p+1);
		printf("%lld\\n",ans);
	}
	return 0;
}
By ❤千柒/Kan_kiz

bzoj4540: [Hnoi2016]序列

4540: [Hnoi2016]序列

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 1266  Solved: 594
[Submit][Status][Discuss]

Description

  给定长度为n的序列:a1,a2,…,an,记为a[1:n]。类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,…,ar-
1
,ar。若1≤l≤s≤t≤r≤n,则称a[s:t]是a[l:r]的子序列。现在有q个询问,每个询问给定两个数l和r,1≤l≤r
≤n,求a[l:r]的不同子序列的最小值之和。例如,给定序列5,2,4,1,3,询问给定的两个数为1和3,那么a[1:3]有
6个子序列a[1:1],a[2:2],a[3:3],a[1:2],a[2:3],a[1:3],这6个子序列的最小值之和为5+2+4+2+2+2=17。

Input

  输入文件的第一行包含两个整数n和q,分别代表序列长度和询问数。接下来一行,包含n个整数,以空格隔开
,第i个整数为ai,即序列第i个元素的值。接下来q行,每行包含两个整数l和r,代表一次询问。

Output

  对于每次询问,输出一行,代表询问的答案。

Sample Input

5 5
5 2 4 1 3
1 5
1 3
2 4
3 5
2 5

Sample Output

28
17
11
11
17

HINT

 

1 ≤N,Q ≤ 100000,|Ai| ≤ 10^9

 
题解
  这题一直没过今天发现单调栈写错了……
  首先我们找出L[i],R[i]表示[L[i],i]和[i,R[i]都是比a[i]大的数,且a[L[i]-1]和a[R[i]+1]都要比a[i]大,这样左端点在[L[i],i],右端点在[i,R[i]]的区间都是以a[i]为最小值。也就是可以认为在二维平面上x坐标是[L[i],i]y坐标是[i,R[i]]的点权值都是a[i],而我们查询的实际上就是二维平面上[l,r][l,r]的矩形权值和。
  L[i]和R[i]可以一遍单调栈轻松求出来。这样剩下的就是一个矩形加矩形求和的问题了,当然可以主席树来搞,但是觉得写的麻烦的我用了奇怪的办法……
  首先把询问拆成(l-1,l..r),(r,l..r)两个,这样询问就是两个求前缀和的操作。用一条扫描线扫过去,矩形拆成加入和删除的删除(l1,l2,r2,+w),(r1+1,l2,r2,-w)。用一颗线段树维护当前扫描线上的权值,加入当前扫描线在i的位置,有询问LR,那么首先计算x=sum1(L,R)*(i+1)的值,但是我们发现这样的话,假如矩形加事件发生在l1那么就会多算(1,l1)的部分。同时,如果一个矩形已经结束,我们会少算(l1,r1)的部分。这样我们可以再维护一颗线段树,事件(pos,l,r,w),每次加的权值是pos*w,最后把x-sum2(L,R)就行了。
  代码比较丑……主要是这个思想对吧……
  
技术分享
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<algorithm>
  6 #define inf 1000000001
  7 using namespace std;
  8 const int N=200085;
  9 long long F[4*N],Tag[4*N],f[4*N],tag[4*N];
 10 long long  ans[N];
 11 long long  a[N],st[N],L[N],R[N];
 12 struct opr{int id,l,r,ps,dd;}op[2*N];
 13 struct mar{int l,r,dd,ps;long long w;}data[2*N];
 14 int n,m,l,r,tt,tot;
 15 inline long long  read()
 16 {
 17     int x=0,f=1;char ch=getchar();
 18     while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();}
 19     while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();}
 20     return x*f;
 21 }
 22 bool cmp1(mar a,mar b)
 23 {
 24     return a.ps<b.ps;
 25 }
 26 bool cmp2(opr a,opr b)
 27 {
 28     return a.ps<b.ps;
 29 }
 30 void INS(int i,int l,int r,int ll,int rr,long long w)
 31 {
 32     if((ll<=l)&&(r<=rr))
 33     {
 34         Tag[i]+=w;F[i]+=w*(long long)(r-l+1);return;
 35     }
 36     int mid=(l+r)/2;
 37     if(ll<=mid)INS(i*2,l,mid,ll,rr,w);if(mid+1<=rr)INS(i*2+1,mid+1,r,ll,rr,w);
 38     F[i]=Tag[i]*(long long)(r-l+1)+F[i*2]+F[i*2+1];
 39 }
 40 void ins(int i,int l,int r,int ll,int rr,long long w)
 41 {
 42     if((ll<=l)&&(r<=rr))
 43     {
 44         tag[i]+=w;f[i]+=w*(long long)(r-l+1);return;
 45     }
 46     int mid=(l+r)/2;
 47     if(ll<=mid)ins(i*2,l,mid,ll,rr,w);if(mid+1<=rr)ins(i*2+1,mid+1,r,ll,rr,w);
 48     f[i]=tag[i]*(long long)(r-l+1)+f[i*2]+f[i*2+1];
 49 }
 50 long long ASK(int i,int l,int r,int ll,int rr)
 51 {
 52     if((ll<=l)&&(r<=rr))return F[i];
 53     int mid=(l+r)/2;long long tmp=Tag[i]*(long long)(min(rr,r)-max(ll,l)+1);
 54     if(ll<=mid)tmp+=ASK(i*2,l,mid,ll,rr);if(mid+1<=rr)tmp+=ASK(i*2+1,mid+1,r,ll,rr);
 55     return tmp;
 56 }
 57 long long ask(int i,int l,int r,int ll,int rr)
 58 {
 59     if((ll<=l)&&(r<=rr))return f[i];
 60     int mid=(l+r)/2;long long tmp=tag[i]*(long long)(min(rr,r)-max(ll,l)+1);
 61     if(ll<=mid)tmp+=ask(i*2,l,mid,ll,rr);if(mid+1<=rr)tmp+=ask(i*2+1,mid+1,r,ll,rr);
 62     return tmp;
 63 }
 64 int main()
 65 {
 66     int n=read(),m=read();
 67     for(int i=1;i<=n;i++)a[i]=read();
 68     a[0]=-inf;
 69     int top=0;st[0]=0;
 70     for(int i=1;i<=n;i++)
 71     {
 72         while(a[st[top]]>=a[i]){L[st[top]]=st[top-1]+1;R[st[top]]=i-1;top--;}
 73         st[++top]=i;
 74     }
 75     while(top){L[st[top]]=st[top-1]+1;R[st[top]]=n;top--;}
 76     for(int i=1;i<=n;i++)
 77     {
 78         if(L[i]<=R[i])
 79         {
 80             data[++tt].w=a[i];data[tt].l=L[i];data[tt].r=i;data[tt].ps=i;data[tt].dd=1;
 81             data[++tt].w=a[i];data[tt].l=L[i];data[tt].r=i;data[tt].ps=R[i]+1;data[tt].dd=-1;
 82         }
 83     }
 84     sort(data+1,data+tt+1,cmp1);
 85     for(int i=1;i<=m;i++)
 86     {
 87         l=read();r=read();
 88         op[++tot].id=i;op[tot].l=l;op[tot].r=r;op[tot].ps=r;op[tot].dd=1;
 89         op[++tot].id=i;op[tot].l=l;op[tot].r=r;op[tot].ps=l-1;op[tot].dd=-1;
 90     }
 91     sort(op+1,op+tot+1,cmp2);int j=1;
 92     for(int i=1;i<=tot;i++)
 93     {
 94         while((data[j].ps<=op[i].ps)&&(j<=tt))
 95         {
 96             long long now=data[j].ps;long long w=now*(long long)data[j].w;
 97             INS(1,1,n,data[j].l,data[j].r,w*data[j].dd);
 98             ins(1,1,n,data[j].l,data[j].r,data[j].w*data[j].dd);
 99             j++;
100         }
101         long long w;
102         w=ask(1,1,n,op[i].l,op[i].r)*(long long)(op[i].ps+1);
103         w=w-ASK(1,1,n,op[i].l,op[i].r);
104         ans[op[i].id]+=w*op[i].dd;
105     }
106     for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
107     return 0;
108 }
View Code

 

4540: [Hnoi2016]序列

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 1266  Solved: 594
[Submit][Status][Discuss]

Description

  给定长度为n的序列:a1,a2,…,an,记为a[1:n]。类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,…,ar-
1
,ar。若1≤l≤s≤t≤r≤n,则称a[s:t]是a[l:r]的子序列。现在有q个询问,每个询问给定两个数l和r,1≤l≤r
≤n,求a[l:r]的不同子序列的最小值之和。例如,给定序列5,2,4,1,3,询问给定的两个数为1和3,那么a[1:3]有
6个子序列a[1:1],a[2:2],a[3:3],a[1:2],a[2:3],a[1:3],这6个子序列的最小值之和为5+2+4+2+2+2=17。

Input

  输入文件的第一行包含两个整数n和q,分别代表序列长度和询问数。接下来一行,包含n个整数,以空格隔开
,第i个整数为ai,即序列第i个元素的值。接下来q行,每行包含两个整数l和r,代表一次询问。

Output

  对于每次询问,输出一行,代表询问的答案。

Sample Input

5 5
5 2 4 1 3
1 5
1 3
2 4
3 5
2 5

Sample Output

28
17
11
11
17

HINT

 

1 ≤N,Q ≤ 100000,|Ai| ≤ 10^9

































以上是关于「HNOI2016」序列的主要内容,如果未能解决你的问题,请参考以下文章

bzoj4540HNOI2016序列

bzoj4540: [Hnoi2016]序列

[BZOJ4540][HNOI2016]序列(莫队)

BZOJ4540HNOI2016序列(莫队)

[HNOI 2016]序列

[HNOI2016]序列