zoukankan      html  css  js  c++  java
  • 洛谷 P2048 [NOI2010]超级钢琴 || Fantasy

    https://www.luogu.org/problemnew/show/P2048

    http://www.lydsy.com/JudgeOnline/problem.php?id=2006

    首先计算出数列的前缀和数组a[0]..a[n],那么问题转化为:从[0,n]中选取任意两个数(i,j)使得i<j,计算a[j]-a[i];对于所有合法的数对(i,j),求第k大的a[j]-a[i]的值。

    考虑枚举i,对于每一个i要得到第k大的a[j]-a[i],则需要从[i+1,n]中选取j使得a[j]是a[i+1]到a[n]中第k大的。

    用一个优先队列维护一下,然后问题就只剩区间k大了。可以用静态主席树搞一下。

    但是你会发现T到飞起...常数太大了2333333

    本来动态开点偷懒都不行了

     1 #pragma GCC optimize("Ofast")
     2 #pragma GCC optimize("inline","fast-math","unroll-loops","no-stack-protector")
     3 #pragma GCC diagnostic error "-fwhole-program"
     4 #pragma GCC diagnostic error "-fcse-skip-blocks"
     5 #pragma GCC diagnostic error "-funsafe-loop-optimizations"
     6 #pragma GCC diagnostic error "-std=c++14"
     7 #include<cstdio>
     8 #include<algorithm>
     9 #include<queue>
    10 #include<tr1/unordered_map>
    11 #define mid ((l+r)>>1)
    12 using namespace std;
    13 typedef long long LL;
    14 typedef pair<LL,int> P;
    15 tr1::unordered_map<LL,int> ma;
    16 LL tmp[500100],ma2[500100];
    17 int mem,lc[40000100],rc[40000100],dat[40000100],root[500100];
    18 LL ll,rr,totn;
    19 LL L;int x;
    20 //root[i]表示[i+L,i+R]区间的线段树,也就是选i+1号为左端点时右端点可取范围
    21 void addx(LL l,LL r,int& num)
    22 {
    23     int t=num;num=++mem;//if(mem>=40000000){puts("test");exit(0);}
    24     lc[num]=lc[t];rc[num]=rc[t];dat[num]=dat[t];
    25     if(l==r)    {dat[num]+=x;return;}
    26     if(L<=mid)    addx(l,mid,lc[num]);
    27     else    addx(mid+1,r,rc[num]);
    28     dat[num]=dat[lc[num]]+dat[rc[num]];
    29 }
    30 LL getx(LL l,LL r,int num)//第x大
    31 {
    32     if(l==r)    return l;
    33     if(dat[rc[num]]>=x)    return getx(mid+1,r,rc[num]);
    34     else    return x-=dat[rc[num]],getx(l,mid,lc[num]);
    35 }
    36 int n,k,lx,rx;LL ans;
    37 LL a[500010];int nownum[500010];
    38 priority_queue<P> q;
    39 //P(当前和,i)(当前和等于[i+L,i+R]的第nownum[i]大-a[i])
    40 //void out(LL l,LL r,LL num)
    41 //{
    42 //    if(l==r)    {for(LL i=1;i<=dat[num];i++)    printf("%lld ",l);return;}
    43 //    out(l,mid,lc[num]);out(mid+1,r,rc[num]);
    44 //}
    45 //void out()
    46 //{
    47 //    queue<P> qq;P t;
    48 //    while(!q.empty())    {t=q.top();q.pop();printf("%lld %lld
    ",t.first,t.second);qq.push(t);}
    49 //    while(!qq.empty())    {t=qq.front();qq.pop();q.push(t);}
    50 //}
    51 int main()
    52 {
    53     int l=0,r=-1,i;P t;
    54     scanf("%d%d%d%d",&n,&k,&lx,&rx);
    55     for(i=1;i<=n;i++)    scanf("%lld",&a[i]),a[i]+=a[i-1],tmp[++tmp[0]]=a[i];
    56     sort(tmp+1,tmp+tmp[0]+1);totn=unique(tmp+1,tmp+tmp[0]+1)-tmp-1;
    57     for(i=1;i<=totn;i++)    ma[tmp[i]]=i,ma2[i]=tmp[i];
    58     for(i=1;i<=n;i++)    a[i]=ma[a[i]];
    59     ll=1;rr=totn;
    60     while(r<n&&r<rx)    r++,L=a[r],x=1,addx(ll,rr,root[0]);
    61     while(l<=n&&l<lx)    L=a[l],x=-1,addx(ll,rr,root[0]),l++;
    62     if(l<=r)
    63     {
    64         x=nownum[0]=1;
    65         q.push(P(ma2[getx(ll,rr,root[0])]-ma2[a[0]],0));
    66     }
    67     for(i=1;i<n;i++)
    68     {
    69         root[i]=root[i-1];
    70         while(r<n&&r<i+rx)    r++,L=a[r],x=1,addx(ll,rr,root[i]);
    71         while(l<=n&&l<i+lx)    L=a[l],x=-1,addx(ll,rr,root[i]),l++;
    72         if(l<=r)
    73         {
    74             x=nownum[i]=1;
    75             q.push(P(ma2[getx(ll,rr,root[i])]-ma2[a[i]],i));
    76         }
    77     }
    78     //out();
    79     //printf("
    %lld",mem);
    80     //return 0;
    81 
    82     for(i=1;i<=k;i++)
    83     {
    84         t=q.top();q.pop();
    85         //printf("a%lld
    ",t.first);
    86         ans+=t.first;
    87         ++nownum[t.second];
    88         //l=min(n+1,t.second+lx);r=min(n,t.second+rx);
    89         //if(r-l+1<nownum[t.second])    continue;
    90         if(dat[root[t.second]]<nownum[t.second])    continue;
    91         x=nownum[t.second];
    92         q.push(P(ma2[getx(ll,rr,root[t.second])]-ma2[a[t.second]],t.second));
    93     }
    94     printf("%lld",ans);
    95 
    96     //for(i=0;i<n;i++)    {out(ll,rr,root[i]);puts("");}
    97     return 0;
    98 }

    错误记录:90行写成dat[t.second];动态开点(没有离散化)时误以为值域只有-1000到1000(但事实上值域是前缀和的值域,-5e8到5e8)

    开了奇怪的Ofast后A掉了....

    其实有更优越的做法,就是维护优先队列时,每次搞完[l,r]区间最大值(在k位置)后,就把它拆成[l,k-1]和[r+1,k](还有这两个区间内分别的最小值)分别放回去,这样恰好去掉了之前的最大值,又不破坏次大值、第三大值、....。这样只需要ST表预处理一下然后RMQ就行了,好写&常数小


    这里有一个一模一样的题,并过来

  • 相关阅读:
    Educational Codeforces Round 86 (Rated for Div. 2) D. Multiple Testcases
    Educational Codeforces Round 86 (Rated for Div. 2) C. Yet Another Counting Problem
    HDU
    HDU
    HDU
    HDU
    Good Bye 2019 C. Make Good (异或的使用)
    Educational Codeforces Round 78 (Rated for Div. 2) C. Berry Jam
    codeforces 909C. Python Indentation
    codeforces1054 C. Candies Distribution
  • 原文地址:https://www.cnblogs.com/hehe54321/p/8586056.html
Copyright © 2011-2022 走看看