「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;
}
bzoj4540: [Hnoi2016]序列
4540: [Hnoi2016]序列
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 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 2 4 1 3
1 5
1 3
2 4
3 5
2 5
Sample Output
17
11
11
17
HINT
1 ≤N,Q ≤ 100000,|Ai| ≤ 10^9
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 }
4540: [Hnoi2016]序列
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 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 2 4 1 3
1 5
1 3
2 4
3 5
2 5
Sample Output
17
11
11
17
HINT
1 ≤N,Q ≤ 100000,|Ai| ≤ 10^9
以上是关于「HNOI2016」序列的主要内容,如果未能解决你的问题,请参考以下文章