zoukankan      html  css  js  c++  java
  • BZOJ2006: [NOI2010]超级钢琴

    $n leq 5e5$的正负数数列,求长度在$[L,R]$的区间和前$K leq 5e5$大的区间和的和。

    先把区间和变为两个前缀和相减。定一移二,确定区间右端点,然后左端点就是一个范围。用个堆把每个右端点能取得的区间和最大的那个丢进去,咋知道哪个最大?右端点前缀和一定,找左端点在一定范围内的最小:

    方法一:如果打算用主席树,那就把三元组$(x,v,k)$丢进堆里,表示右端点为$x$,某个区间和为$v$,这个区间是$x$为右端点时对应范围的第$k$大的区间,每次取出时把$x$为右端点对应区间的第$k+1$大丢进堆里。

     1 //#include<iostream>
     2 #include<cstring>
     3 #include<cstdlib>
     4 #include<cstdio>
     5 #include<queue>
     6 //#include<time.h>
     7 //#include<complex>
     8 #include<algorithm>
     9 #include<stdlib.h>
    10 using namespace std;
    11 
    12 int n,K,ll,rr;
    13 #define maxn 500011
    14 int sum[maxn],a[maxn];
    15 
    16 struct qnode{int id,v,k; bool operator < (const qnode &b) const {return v<b.v;}};
    17 priority_queue<qnode> q;
    18 
    19 int root[maxn];
    20 struct SMT
    21 {
    22     struct Node
    23     {
    24         int ls,rs;
    25         int cnt;
    26     }a[maxn*20];
    27     int size,n;
    28     void clear(int m) {n=m; size=0;}
    29     void up(int x) {a[x].cnt=a[a[x].ls].cnt+a[a[x].rs].cnt;}
    30     void insert(int &x,int y,int L,int R,int v)
    31     {
    32         x=++size; a[x].cnt=a[y].cnt+1;
    33         if (L==R) {a[x].ls=a[x].rs=0; return;}
    34         int mid=(L+R)>>1;
    35         if (v<=mid) insert(a[x].ls,a[y].ls,L,mid,v),a[x].rs=a[y].rs;
    36         else insert(a[x].rs,a[y].rs,mid+1,R,v),a[x].ls=a[y].ls;
    37     }
    38     void insert(int &x,int y,int v) {insert(x,y,1,n,v);}
    39     int kth(int x,int y,int k)
    40     {
    41         if (k>a[y].cnt-a[x].cnt) return 0;
    42         int L=1,R=n;
    43         while (L<R)
    44         {
    45             int mid=(L+R)>>1;
    46             if (a[a[y].ls].cnt-a[a[x].ls].cnt>=k) R=mid,x=a[x].ls,y=a[y].ls;
    47             else k-=a[a[y].ls].cnt-a[a[x].ls].cnt,L=mid+1,x=a[x].rs,y=a[y].rs;
    48         }
    49         return L;
    50     }
    51 }t;
    52 
    53 #define LL long long
    54 int lisa[maxn],li=0;
    55 int main()
    56 {
    57     scanf("%d%d%d%d",&n,&K,&ll,&rr);
    58     lisa[++li]=sum[0]; for (int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i],lisa[++li]=sum[i];
    59     sort(lisa+1,lisa+1+li); li=unique(lisa+1,lisa+1+li)-lisa-1;
    60     t.clear(li); t.insert(root[0],root[n+3],(sum[0]=lower_bound(lisa+1,lisa+1+li,sum[0])-lisa));
    61     for (int i=1;i<=n;i++) t.insert(root[i],root[i-1],(sum[i]=lower_bound(lisa+1,lisa+1+li,sum[i])-lisa));
    62     for (int i=ll;i<=n;i++) q.push((qnode){i,lisa[sum[i]]-lisa[t.kth(root[i-rr-1<0?n+3:i-rr-1],root[i-ll],1)],1});
    63     
    64     LL ans=0;
    65     for (int i=1;i<=K;i++)
    66     {
    67         qnode now=q.top(); q.pop();
    68         ans+=now.v;
    69         int tmp;
    70         if ((tmp=t.kth(root[now.id-rr-1<0?n+3:now.id-rr-1],root[now.id-ll],now.k+1))>0)
    71             q.push((qnode){now.id,lisa[sum[now.id]]-lisa[tmp],now.k+1});
    72     }
    73     printf("%lld
    ",ans);
    74     return 0;
    75 }
    View Code

    方法二:如果打算有st表,每次取掉堆中一个元素后,由于st表不支持删除,那就把那个区间以删除的点为界劈成两半再丢堆里,$(x,v,a,b,c)$表示右端点$x$,当前区间和$v$,是在$[a,b]$区间找到的左端点$c$。

     1 //#include<iostream>
     2 #include<cstring>
     3 #include<cstdlib>
     4 #include<cstdio>
     5 #include<queue>
     6 //#include<time.h>
     7 //#include<complex>
     8 #include<algorithm>
     9 #include<stdlib.h>
    10 using namespace std;
    11 
    12 int n,K,ll,rr;
    13 #define maxn 500011
    14 int sum[maxn],a[maxn];
    15 
    16 struct qnode{int id,v,a,b,c; bool operator < (const qnode &b) const {return v<b.v;}};
    17 priority_queue<qnode> q;
    18 
    19 int st[maxn][22],Log[maxn];
    20 int query(int x,int y)
    21 {
    22     int l=Log[y-x+1];
    23     return sum[st[x][l]]<sum[st[y-(1<<l)+1][l]]?st[x][l]:st[y-(1<<l)+1][l];
    24 }
    25 
    26 #define LL long long
    27 int main()
    28 {
    29     scanf("%d%d%d%d",&n,&K,&ll,&rr);
    30     st[0][0]=0;
    31     for (int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i],st[i][0]=i;
    32     Log[0]=-1; for (int i=1;i<=n+1;i++) Log[i]=Log[i>>1]+1;
    33     for (int j=1;j<=20;j++)
    34         for (int i=0,to=(n-(1<<j)+1);i<=to;i++)
    35             st[i][j]=sum[st[i][j-1]]<sum[st[i+(1<<(j-1))][j-1]]?st[i][j-1]:st[i+(1<<(j-1))][j-1];
    36     for (int i=ll;i<=n;i++)
    37     {
    38         int tmp=query(max(0,i-rr),i-ll);
    39         q.push((qnode){i,sum[i]-sum[tmp],max(0,i-rr),i-ll,tmp});
    40     }
    41     LL ans=0;
    42     for (int i=1;i<=K;i++)
    43     {
    44         qnode now=q.top(); q.pop();
    45         ans+=now.v;
    46         int tmp;
    47         if (now.c>now.a)
    48         {
    49             tmp=query(now.a,now.c-1);
    50             q.push((qnode){now.id,sum[now.id]-sum[tmp],now.a,now.c-1,tmp});
    51         }
    52         if (now.c<now.b)
    53         {
    54             tmp=query(now.c+1,now.b);
    55             q.push((qnode){now.id,sum[now.id]-sum[tmp],now.c+1,now.b,tmp});
    56         }
    57     }
    58     printf("%lld
    ",ans);
    59     return 0;
    60 }
    View Code
  • 相关阅读:
    POJ 1401 Factorial
    POJ 2407 Relatives(欧拉函数)
    POJ 1730 Perfect Pth Powers(唯一分解定理)
    POJ 2262 Goldbach's Conjecture(Eratosthenes筛法)
    POJ 2551 Ones
    POJ 1163 The Triangle
    POJ 3356 AGTC
    POJ 2192 Zipper
    POJ 1080 Human Gene Functions
    POJ 1159 Palindrome(最长公共子序列)
  • 原文地址:https://www.cnblogs.com/Blue233333/p/8568489.html
Copyright © 2011-2022 走看看