[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 }

 

posted @ 2017-10-12 12:24  Z-Y-Y-S  阅读(531)  评论(0编辑  收藏  举报