bzoj4504 K个串

传送门

好题啊……

问题是询问k大子串和,不难想到区间裂解的做法(把以每个位置结尾的初始最优解和左端点可行区间都扔到大根堆里,每次取堆顶并把可行区间分裂成两个,把分裂出的两个左端点可行区间及其最优解扔回堆里)。然后还需要一个数据结构来资瓷在线查询给定右端点且左端点限定在一个区间内时最大的子串和以及对应的左端点(似乎有点绕……),应该不难想到对每个右端点都建一棵线段树维护左端点取在每个位置时的子串和及其区间最大值。注意到每个右端点对应的线段树是可以从上一个版本的线段树快速更新得到的(设这个位置的数上次出现的位置是p,那么左端点取在p及左边时子串和不会变化,去在p+1到当前位置这一段时答案会加上这个数,因此把p+1~当前位置这个区间都加上这个数即可),主席树压内存即可。这里有区间修改,因此需要标记永久化或者访问时下传标记+动态开点,似乎标记永久化常数要小一些?好吧我并没有试过下传标记的写法……(对于区间加一个数等操作是可以写标记永久化的,不过区间覆盖之类的操作就没法标记永久化了……)

(我越看越觉得这么多东西挤在一段很难看……然而懒得手动分段了,就这样吧……)

一开始以为这题的主席树会很难写,然而调了一会儿也就调出来了……因为标记永久化了所以查询写的比较丑,虽然跑的也不是很慢吧……

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<queue>
  5 #include<map>
  6 using namespace std;
  7 const int maxn=100010;
  8 struct A{
  9     int x,y,l,r;
 10     long long sum;
 11     A(int x,int y,int l,int r,long long sum):x(x),y(y),l(l),r(r),sum(sum){}
 12     bool operator<(const A &a)const{return sum<a.sum;}
 13 };
 14 void build0(int,int,int&);
 15 void build(int,int,int&,int);
 16 pair<int,long long>query(int,int,int);
 17 long long mx[maxn<<6]={0},lazy[maxn<<6]={0},d;
 18 int lc[maxn<<6]={0},rc[maxn<<6]={0},id[maxn<<6]={0},root[maxn]={0},cnt=0;
 19 map<int,int>last;
 20 priority_queue<A>heap;
 21 int n,k,a[maxn],x,s,t;
 22 int main(){
 23     freopen("bzoj_4504.in","r",stdin);
 24     freopen("bzoj_4504.out","w",stdout);
 25     scanf("%d%d",&n,&k);
 26     build0(1,n,root[0]);
 27     for(int i=1;i<=n;i++){
 28         scanf("%d",&a[i]);
 29         s=last[a[i]]+1;
 30         t=i;
 31         d=a[i];
 32         build(1,n,root[i],root[i-1]);
 33         s=1;
 34         pair<int,long long>tmp=query(1,n,root[i]);
 35         heap.push(A(i,tmp.first,1,i,tmp.second));
 36         last[a[i]]=i;
 37     }
 38     while(--k){
 39         A z=heap.top();
 40         heap.pop();
 41         if(z.l<z.y){
 42             s=z.l;
 43             t=z.y-1;
 44             pair<int,long long>tmp=query(1,n,root[z.x]);
 45             heap.push(A(z.x,tmp.first,z.l,z.y-1,tmp.second));
 46         }
 47         if(z.r>z.y){
 48             s=z.y+1;
 49             t=z.r;
 50             pair<int,long long>tmp=query(1,n,root[z.x]);
 51             heap.push(A(z.x,tmp.first,z.y+1,z.r,tmp.second));
 52         }
 53     }
 54     printf("%lld",heap.top().sum);
 55     return 0;
 56 }
 57 void build0(int l,int r,int &rt){
 58     id[rt=++cnt]=l;
 59     if(l==r)return;
 60     int mid=(l+r)>>1;
 61     build0(l,mid,lc[rt]);
 62     build0(mid+1,r,rc[rt]);
 63 }
 64 void build(int l,int r,int &rt,int pr){
 65     rt=++cnt;
 66     lc[rt]=lc[pr];
 67     rc[rt]=rc[pr];
 68     lazy[rt]=lazy[pr];
 69     if(s<=l&&t>=r){
 70         mx[rt]=mx[pr]+d;
 71         lazy[rt]+=d;
 72         id[rt]=id[pr];
 73         return;
 74     }
 75     int mid=(l+r)>>1;
 76     if(s<=mid)build(l,mid,lc[rt],lc[pr]);
 77     if(t>mid)build(mid+1,r,rc[rt],rc[pr]);
 78     if(mx[lc[rt]]>=mx[rc[rt]]){
 79         mx[rt]=mx[lc[rt]]+lazy[rt];
 80         id[rt]=id[lc[rt]];
 81     }
 82     else{
 83         mx[rt]=mx[rc[rt]]+lazy[rt];
 84         id[rt]=id[rc[rt]];
 85     }
 86 }
 87 pair<int,long long>query(int l,int r,int rt){
 88     if(s<=l&&t>=r)return make_pair(id[rt],mx[rt]);
 89     int mid=(l+r)>>1;
 90     if(t<=mid){
 91         pair<int,long long>tmp=query(l,mid,lc[rt]);
 92         tmp.second+=lazy[rt];
 93         return tmp;
 94     }
 95     else if(s>mid){
 96         pair<int,long long>tmp=query(mid+1,r,rc[rt]);
 97         tmp.second+=lazy[rt];
 98         return tmp;
 99     }
100     else{
101         pair<int,long long>tmpl=query(l,mid,lc[rt]),tmpr=query(mid+1,r,rc[rt]);
102         tmpl.second+=lazy[rt];
103         tmpr.second+=lazy[rt];
104         return tmpl.second>=tmpr.second?tmpl:tmpr;
105     }
106 }
View Code

 

posted @ 2017-02-15 18:06  AntiLeaf  阅读(199)  评论(0编辑  收藏  举报