[AH/HNOI2017]影魔

题目背景

影魔,奈文摩尔,据说有着一个诗人的灵魂。 事实上,他吞噬的诗人灵魂早已成千上万。

千百年来,他收集了各式各样的灵魂,包括诗人、 牧师、 帝王、 乞丐、 奴隶、 罪人,当然,还有英雄。

题目描述

每一个灵魂,都有着自己的战斗力,而影魔,靠这些战斗力提升自己的攻击。

奈文摩尔有 n 个灵魂,他们在影魔宽广的体内可以排成一排,从左至右标号 1 到 n。第 i个灵魂的战斗力为 k[i],灵魂们以点对的形式为影魔提供攻击力,对于灵魂对 i, j(i<j)来说,若不存在 ks大 于 k[i]或者 k[j],则会为影魔提供 p1 的攻击力(可理解为: 当 j=i+1 时,因为不存在满足 i<s<j 的 s,从而 k[s]不存在,这时提供 p1 的攻击力;当 j>i+1 时,若max{k[s]|i<s<j}<=min{k[i],k[j]} , 则 提 供 p1 的 攻 击 力 ); 另 一 种 情 况 , 令 c 为k[i+1],k[i+2],k[i+3]……k[j-1]的最大值,若 c 满足: k[i]<c<k[j],或者 k[j]<c<k[i],则会为影魔提供 p2 的攻击力,当这样的 c 不存在时,自然不会提供这 p2 的攻击力;其他情况的点对,均不会为影魔提供攻击力。

影魔的挚友噬魂鬼在一天造访影魔体内时被这些灵魂吸引住了,他想知道,对于任意一段区间[a,b], 1<=a<b<=n,位于这些区间中的灵魂对会为影魔提供多少攻击力,即考虑 所有满足a<=i<j<=b 的灵魂对 i,j 提供的攻击力之和。

顺带一提,灵魂的战斗力组成一个 1 到 n 的排列: k[1],k[2],…,k[n]。

输入输出格式

输入格式:

输入文件名为 sf.in。

第一行 n,m,p1,p2

第二行 n 个数: k[1],k[2],…,k[n]

接下来 m 行, 每行两个数 a,b, 表示询问区间[a,b]中的灵魂对会为影魔提供多少攻击力。

输出格式:

输出文件名为 sf.out

共输出 m 行,每行一个答案,依次对应 m 个询问。

输入输出样例

输入样例#1:
10 5 2 3
7 9 5 1 3 10 6 8 2 4
1 7
1 9
1 3
5 9
1 5
输出样例#1:
30
39
4
13
16

说明

30%: 1<= n,m <= 500。

另 30%: p1=2*p2。

100%:1 <= n,m <= 200000; 1 <= p1,p2 <= 1000。

补上一个更好理解的方法:主席树

 

令L[i]为i前第一个比a[i]大的一位,R[i]为i后第一个比a[i]大的一位

 

1.每组(R[i],L[i])  (i,i+1)可以贡献p1

 

2.(L[i],j) (i+1<=j<=R[i]-1)  (R[i],j)(L[i]+1<=j<=i-1) 都可以贡献p2
把他们转化为点(c,p,l,r)表示p与[l,r]构成第c种贡献
按p排序后按顺序建主席树
查询时查询[l,r]线段树内[l,r]区间的和,再加上(i,i+1)的方案

 

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<cmath>
  6 using namespace std;
  7 typedef long long lol;
  8 struct ZYYS
  9 {
 10   int c,p,l,r;
 11 }q[900005];
 12 int pos,ch[5000001][2],root[200005],a[200005],sta[200005],top,n,m,lst[200005],nxt[200005],cnt;
 13 lol sum[5000001],lazy[5000001],p1,p2;
 14 bool cmp(ZYYS a,ZYYS b)
 15 {
 16   return a.p<b.p;
 17 }
 18 void update(int &rt,int l,int r,int L,int R,lol k,int last)
 19 {
 20   int x=rt;
 21   if (rt<=last)
 22    {
 23      rt=++pos;
 24      ch[rt][0]=ch[x][0];ch[rt][1]=ch[x][1];
 25      sum[rt]=sum[x];
 26      lazy[rt]=lazy[x];
 27    }
 28   if (l>=L&&r<=R)
 29     {
 30       sum[rt]+=(r-l+1)*k;
 31       lazy[rt]+=k;
 32       return ;
 33     }
 34   int mid=(l+r)/2;
 35   if (L<=mid) update(ch[rt][0],l,mid,L,R,k,last);
 36   if (R>mid) update(ch[rt][1],mid+1,r,L,R,k,last);
 37   sum[rt]=sum[ch[rt][0]]+sum[ch[rt][1]]+(r-l+1)*lazy[rt];
 38 }
 39 lol query(int x,int y,int l,int r,int L,int R,lol la)
 40 {
 41   if (l>=L&&r<=R)
 42     {
 43       return sum[y]-sum[x]+la*(r-l+1);
 44     }
 45   int mid=(l+r)/2;
 46   lol s=0;
 47   if (L<=mid) s+=query(ch[x][0],ch[y][0],l,mid,L,R,la+lazy[y]-lazy[x]);
 48   if (R>mid) s+=query(ch[x][1],ch[y][1],mid+1,r,L,R,la+lazy[y]-lazy[x]);
 49   return s;
 50 }
 51 int main()
 52 {int i,x,l,r,last=0;
 53   cin>>n>>m>>p1>>p2;
 54   for (i=1;i<=n;i++)
 55       scanf("%d",&a[i]);
 56   top=0;
 57   for (i=1;i<=n;i++)
 58     {
 59       while (top&&a[sta[top]]<a[i]) top--;
 60     lst[i]=sta[top];
 61       top++;
 62       sta[top]=i;
 63     }
 64     top=0;
 65     sta[0]=n+1;
 66   for (i=n;i>=1;i--)
 67     {
 68       while (top&&a[sta[top]]<a[i]) top--;
 69     nxt[i]=sta[top];
 70       top++;
 71       sta[top]=i;
 72     }
 73   for (i=1;i<=n;i++)
 74     {
 75       if (lst[i]&&nxt[i]<=n)
 76     {
 77       q[++cnt]=(ZYYS){1,lst[i],nxt[i],nxt[i]};
 78       q[++cnt]=(ZYYS){1,nxt[i],lst[i],lst[i]};
 79     }
 80       if (lst[i]&&nxt[i]-i>1)
 81     q[++cnt]=(ZYYS){2,lst[i],i+1,nxt[i]-1};
 82       if (nxt[i]<=n&&i-lst[i]>1)
 83     q[++cnt]=(ZYYS){2,nxt[i],lst[i]+1,i-1};
 84     }
 85   sort(q+1,q+cnt+1,cmp);
 86   x=0;
 87   last=0;
 88   for (i=1;i<=cnt;i++)
 89     {
 90       while (x<q[i].p) root[x+1]=root[x],x++,last=pos;
 91       if (q[i].c==1)
 92     update(root[x],1,n,q[i].l,q[i].r,p1,last);
 93       else update(root[x],1,n,q[i].l,q[i].r,2*p2,last);
 94     }
 95   while (x<n) root[x+1]=root[x],x++;
 96   for (i=1;i<=m;i++)
 97     {
 98       scanf("%d%d",&l,&r);
 99       printf("%lld\n",query(root[l-1],root[r],1,n,l,r,0)/2+(r-l)*p1);
100     }
101 }
View Code
线段树:
统计:
我们假设i和[i+1,R[i]-1]之间的数都可以搭配成p2的条件,然后我们就在线段树中把[i+1,R[i]-1]加上p2.
因为(i,R[i])在反方向算作了p2,所以应该+p1-p2,这样反向算时等价于只+p1
对于i和L[i],把数列和询问反转,在做一次相同操作
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 using namespace std;
  6 struct Ask
  7 {
  8     int l,r,id;
  9 }q[200001];
 10 int a[200001],aa[200001],n,m,stack[200001],R[200001];
 11 long long c[800001],mark[800001],ans[200001],p1,p2;
 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 work()
 60 {int i;
 61 memset(c,0,sizeof(c));
 62 memset(mark,0,sizeof(mark));
 63     sort(q+1,q+m+1,cmp);
 64     int top=0;
 65     stack[0]=n+1;
 66     for (i=n;i>=1;i--)
 67     {
 68         while (top&&a[i]>=a[stack[top]]) top--;
 69         R[i]=stack[top];
 70         stack[++top]=i;
 71     }
 72     top=m;
 73     for (i=n;i>=1;i--)
 74     {
 75         if (i+1<=R[i]-1)
 76         change(1,1,n,i+1,R[i]-1,p2);
 77         change(1,1,n,R[i],R[i],p1-p2);
 78         while (top&&q[top].l==i) 
 79         ans[q[top].id]+=getsum(1,1,n,1,q[top].r),top--;
 80     }
 81 }
 82 void rev()
 83 {int i;
 84     for (i=1;i<=n;i++)
 85     aa[i]=a[n-i+1];
 86     for (i=1;i<=n;i++)
 87     a[i]=aa[i];
 88 }
 89 int main()
 90 {int i;
 91     cin>>n>>m>>p1>>p2;
 92     for (i=1;i<=n;i++)
 93     {
 94         scanf("%d",&a[i]);
 95     }
 96     for (i=1;i<=m;i++)
 97     {
 98         scanf("%d%d",&q[i].l,&q[i].r);
 99         q[i].id=i; 
100     }
101     work();
102     rev();
103     for (i=1;i<=m;i++)
104     q[i].l=n+1-q[i].l,q[i].r=n+1-q[i].r,swap(q[i].l,q[i].r);
105     work();
106     for (i=1;i<=m;i++)
107     printf("%lld\n",ans[i]);
108 }

庆祝FGO第一/二个SSR

posted @ 2017-10-05 08:40  Z-Y-Y-S  阅读(317)  评论(1编辑  收藏  举报