[HNOI2016]序列
题目描述
给定长度为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。
输入输出格式
输入格式:输入文件的第一行包含两个整数n和q,分别代表序列长度和询问数。接下来一行,包含n个整数,以空格隔开,第i个整数为ai,即序列第i个元素的值。接下来q行,每行包含两个整数l和r,代表一次询问。
输出格式:对于每次询问,输出一行,代表询问的答案。
输入输出样例
输入样例#1:
5 5 5 2 4 1 3 1 5 1 3 2 4 3 5 2 5
输出样例#1:
28 17 11 11 17
说明
1 <=N,Q <= 100000,|Ai| <= 10^9
网上给出的大多是两种:莫队和线段树+矩阵
可惜我这个菜鸡看不懂
这里给出一种近似暴力的方法
我们模仿影魔的线段树解法:
离线,把询问按l从小到大
R[i]表示i右边第一个比它小的位置
显然子序列[i,i~R[i]-1]的答案都是a[i],维护一个线段树,给i~R[i]-1加上a[i]
我们从n开始从后往前计算,当有询问左端点在i时
求出1~右端点的和
但是我们没有考虑i与R[i]之后的解,而且R[i]这样显然只会与在他后面形成子序列
所以递归把子序列[i,R[i]~R[R[i]]-1].......都加上a[R[i]]
但是这样如果碰到有序递减的序列会变成O(n^2logn)
但是这种省选题数据大多是随机的,所以可以过
有时间会补上莫队做法
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 struct Ask 7 { 8 int l;int r;int id; 9 }ask[100001]; 10 long long c[800001],mark[800001],ans[200001],a[100001],aa[100001]; 11 int n,q,stack[100001],R[100001],flag; 12 bool cmp(Ask a,Ask b) 13 { 14 return a.l<b.l; 15 } 16 void pushup(int rt) 17 { 18 c[rt]=c[rt*2]+c[rt*2+1]; 19 } 20 void pushdown(int rt,int l,int r,int mid) 21 { 22 if (mark[rt]) 23 { 24 mark[rt*2]+=mark[rt]; 25 mark[rt*2+1]+=mark[rt]; 26 c[rt*2]+=mark[rt]*(mid-l+1); 27 c[rt*2+1]+=mark[rt]*(r-mid); 28 mark[rt]=0; 29 } 30 } 31 void change(int rt,int l,int r,int L,int R,long long d) 32 { 33 if (l>=L&&r<=R) 34 { 35 mark[rt]+=d; 36 c[rt]+=(r-l+1)*d; 37 return; 38 } 39 pushdown(rt,l,r,(l+r)/2); 40 int mid=(l+r)/2; 41 if (L<=mid) change(rt*2,l,mid,L,R,d); 42 if (R>mid) change(rt*2+1,mid+1,r,L,R,d); 43 pushup(rt); 44 } 45 long long getsum(int rt,int l,int r,int L,int R) 46 { 47 if (l>=L&&r<=R) 48 { 49 return c[rt]; 50 } 51 int mid=(l+r)/2; 52 pushdown(rt,l,r,mid); 53 long long s=0; 54 if (L<=mid) s+=getsum(rt*2,l,mid,L,R); 55 if (R>mid) s+=getsum(rt*2+1,mid+1,r,L,R); 56 pushup(rt); 57 return s; 58 } 59 void rev() 60 {int i; 61 for (i=1;i<=n;i++) 62 aa[i]=a[n-i+1]; 63 for (i=1;i<=n;i++) 64 a[i]=aa[i]; 65 } 66 void zyys(int x) 67 { 68 while (x<=n-1) 69 { 70 int l=x,r=R[x]-1; 71 change(1,1,n,l,r,a[l]); 72 x=R[x]; 73 } 74 } 75 void work() 76 {int top,i; 77 memset(c,0,sizeof(c)); 78 memset(mark,0,sizeof(mark)); 79 sort(ask+1,ask+q+1,cmp); 80 top=0,stack[top]=n+1; 81 for (i=n;i>=1;i--) 82 { 83 while (top&&a[i]<a[stack[top]]) top--; 84 R[i]=stack[top]; 85 stack[++top]=i; 86 } 87 n++; 88 top=q; 89 for (i=n;i>=1;i--) 90 { 91 zyys(i); 92 while (top&&i==ask[top].l) ans[ask[top].id]+=getsum(1,1,n,1,ask[top].r),top--; 93 } 94 } 95 int main() 96 {int i; 97 cin>>n>>q; 98 for (i=1;i<=n;i++) 99 { 100 scanf("%lld",&a[i]); 101 } 102 for (i=1;i<=q;i++) 103 { 104 scanf("%d%d",&ask[i].l,&ask[i].r); 105 ask[i].id=i; 106 } 107 work(); 108 for (i=1;i<=q;i++) 109 printf("%lld\n",ans[i]); 110 }