Bzoj4504: K个串
题解: 我们考虑到查询区间类不同数目的个数在线做法是主席树维护,然后我们可以维护出每个位置的值产生贡献的范围,然后相当于主席树维护了以i为右端点,[j,i]的不同数字的和 然后考虑到这题的K在可接受的范围内 所以我们采用分裂的方式 即维护每个右端点里面的最大值 然后优先队列维护五元组 找出第K大 时间复杂度是(n+k)logn
#include <bits/stdc++.h> #define ll long long #define pii pair<ll,int> const int MAXN=1e5+10; const ll inf=1e18; using namespace std; ll read(){ ll x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return f*x; } int n,k; typedef struct node{ int l,r,pos;ll sum,maxx; }node; node d[MAXN*101]; int rt[MAXN]; int cnt;int cnt1; void up(int x,int l,int r){ d[x].maxx=d[d[x].l].maxx;d[x].pos=l; if(d[x].l)d[x].pos=d[d[x].l].pos; if(d[d[x].r].maxx>d[x].maxx)d[x].maxx=d[d[x].r].maxx,d[x].pos=d[d[x].r].pos; if(!d[x].pos)d[x].pos=((l+r)>>1)+1; d[x].maxx+=d[x].sum; } void update(int &x,int y,int l,int r,int ql,int qr,ll vul){ // cout<<l<<"====="<<r<<" "<<vul<<" "<<ql<<" "<<qr<<endl; x=++cnt;d[x]=d[y]; if(!d[x].pos)d[x].pos=l; if(ql<=l&&r<=qr){d[x].sum+=vul;d[x].maxx+=vul;return ;} int mid=(l+r)>>1; if(ql<=mid)update(d[x].l,d[y].l,l,mid,ql,qr,vul); if(qr>mid)update(d[x].r,d[y].r,mid+1,r,ql,qr,vul); up(x,l,r); // cout<<d[x].maxx<<"::::"<<" "<<d[x].pos<<" "<<ql<<" "<<qr<<endl; } pii aim;bool flag; void querty(int x,int l,int r,int ql,int qr,ll key){ // cout<<l<<" "<<r<<" "<<ql<<" "<<qr<<" "<<aim.first<<" "<<aim.second<<" "<<d[x].maxx<<endl; if(!x){ if(!flag){ aim.first=key,aim.second=max(l,ql),flag=1; } else{ if(aim.first<key)aim.first=key,aim.second=max(l,ql); } return ; } if(ql<=l&&r<=qr){ if(!flag)aim.first=key+d[x].maxx,aim.second=d[x].pos,flag=1; else{if(aim.first<key+d[x].maxx)aim.first=key+d[x].maxx,aim.second=d[x].pos;} return ; } int mid=(l+r)>>1; if(ql<=mid)querty(d[x].l,l,mid,ql,qr,key+d[x].sum); if(qr>mid)querty(d[x].r,mid+1,r,ql,qr,key+d[x].sum); } vector<ll>vec; ll a[MAXN]; int pre[MAXN]; typedef struct Node{ int l,r,rx,pos;ll vul; friend bool operator<(Node aa,Node bb){ return aa.vul<bb.vul; } }Node; priority_queue<Node>que; int main(){ n=read();k=read(); for(int i=1;i<=n;i++)a[i]=read(),vec.push_back(a[i]); sort(vec.begin(),vec.end()); int sz=unique(vec.begin(),vec.end())-vec.begin(); for(int i=1;i<=n;i++)a[i]=lower_bound(vec.begin(),vec.begin()+sz,a[i])-vec.begin()+1; for(int i=1;i<=n;i++){update(rt[i],rt[i-1],1,n,pre[a[i]]+1,i,vec[a[i]-1]);pre[a[i]]=i;} for(int i=1;i<=n;i++){ Node t;t.l=1;t.r=i;t.rx=i; aim.first=0;aim.second=0; flag=0;querty(rt[i],1,n,1,i,0); t.pos=aim.second;t.vul=aim.first; // cout<<t.pos<<"======------"<<t.vul<<endl; que.push(t); } for(int i=1;i<k;i++){ Node t=que.top();que.pop(); if(t.pos>t.l){ Node t1;t1.l=t.l;t1.r=t.pos-1;t1.rx=t.rx; flag=0;querty(rt[t.rx],1,n,t.l,t.pos-1,0); t1.pos=aim.second;t1.vul=aim.first; que.push(t1); } if(t.pos<t.r){ Node t2;t2.l=t.pos+1;t2.r=t.r;t2.rx=t.rx; flag=0;querty(rt[t.rx],1,n,t.pos+1,t.r,0); t2.pos=aim.second;t2.vul=aim.first; que.push(t2); } } printf("%lld\n",que.top().vul); return 0; }
4504: K个串
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 517 Solved: 200
[Submit][Status][Discuss]
Description
兔子们在玩k个串的游戏。首先,它们拿出了一个长度为n的数字序列,选出其中的一
个连续子串,然后统计其子串中所有数字之和(注意这里重复出现的数字只被统计一次)。
兔子们想知道,在这个数字序列所有连续的子串中,按照以上方式统计其所有数字之和,第
k大的和是多少。
Input
第一行,两个整数n和k,分别表示长度为n的数字序列和想要统计的第k大的和
接下里一行n个数a_i,表示这个数字序列
Output
一行一个整数,表示第k大的和
Sample Input
7 5
3 -2 1 2 2 1 3 -2
3 -2 1 2 2 1 3 -2
Sample Output
4
HINT
1 <= n <= 100000, 1 <= k <= 200000, 0 <= |a_i| <= 10^9数据保证存在第 k 大的和