zoukankan      html  css  js  c++  java
  • [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
    ",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
    ",ans[i]);
    108 }

    庆祝FGO第一/二个SSR

  • 相关阅读:
    ACM-ICPC 2018 南京赛区网络预赛J. Sum
    ACM-ICPC 2018 南京赛区网络预赛 L. Magical Girl Haze(分层dijkstra)
    51nod1246 罐子和硬币
    UVALive
    CodeForces
    CodeForces
    hdu3861(tarjan缩点+最小路径覆盖)
    hdu1072(dfs和bfs)
    51nod1352 集合计数(扩展欧几里得)
    Cmder如何调整命令行字体大小
  • 原文地址:https://www.cnblogs.com/Y-E-T-I/p/7628680.html
Copyright © 2011-2022 走看看