[bzoj P4504] K个串
【题目描述】
兔子们在玩k个串的游戏。首先,它们拿出了一个长度为n的数字序列,选出其中的一个连续子串,然后统计其子串中所有数字之和(注意这里重复出现的数字只被统计一次)。
兔子们想知道,在这个数字序列所有连续的子串中,按照以上方式统计其所有数字之和,第k大的和是多少。
【输入格式】
第一行,两个整数n和k,分别表示长度为n的数字序列和想要统计的第k大的和
接下里一行n个数a_i,表示这个数字序列
【输出格式】
一行一个整数,表示第k大的和
【样例输入】
7 5
3 -2 1 2 2 1 3 -2
【样例输出】
4
【数据范围】
对于20%的数据,1 <= n <= 2000
对于另外20%的数据,0 <= a_i <= 10^9
对于100%的数据,1 <= n <= 100000, 1 <= k <= 200000, 0 <= |a_i| <= 10^9
数据保证存在第k大的和
题外话:好久都没有用博客园啦,最近写博客都写在WordPress上面。没想到今天炸了。。只好先来cnblogs避一避了。
对于这一题我也是一脸懵逼式的弃疗。连想都没怎么想,丝毫没有办法。
结果——主席树+堆。也是看了题解才明白的。自己怎么也想不到这种方法。
主思路是这样的,维护一个五元组(v,l,r,x,p)表示一个状态,分别表示当前状态所对应区间的和(v),左端点所在区间(l,r),左端点的具体位置(x),右端点的具体位置(p)。(这个思路骑士真的太难想到了,主要一个我觉得,习惯于考虑对称的东西,不会像这样,区间左右端点有别)
先不考虑如何如何构造或得到五元组。假设我们可以很快得到某一个特定的五元组。那如何得出第k大的和?
每一次从堆中取出最大值,也就是当前最大的和,然后做这样的事情:
构造五元组(maxsum_val(),l,p-1,maxsum_pos(),p)和(maxsum_val(),p+1,r,maxsum_pos(),p),并将它们push入堆中。显然这是正确的。
那刚开始在堆里的是什么呢?当然是p=1~n时,左端点在某一点能使区间和最大的这个状态咯。
这个问题——涉及到区间修改,区间查询。肯定要用线段树咯。但是我们发现用线段树是无法处理对于不同的右端点p,查询某个区间最值的问题的。
所以我们对于每一种右端点p,建立一颗主席树,然后在对应的主席树上高即可。
code:
1 #pragma GCC optimize(2) 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <map> 6 #include <queue> 7 #define LL long long 8 #define mp make_pair 9 #define pli pair <LL,int> 10 11 using namespace std; 12 13 const int N=100005,M=10000005; 14 15 int n,m; map <int,int> pre; 16 struct node { 17 LL v; int x,l,r,p; 18 node () {} 19 node (LL _v,int _x,int _l,int _r,int _p) : 20 v(_v),x(_x),l(_l),r(_r),p(_p) {} 21 bool operator < (const node &o) const { 22 return v<o.v; 23 } 24 }; 25 priority_queue <node> q; 26 27 namespace TREE { 28 int tot,rt[N],lc[M],rc[M]; pli w[M]; LL tag[M]; 29 #define mid (((l)+(r))>>1) 30 #define ms(a,x) memset(a,x,sizeof a) 31 inline void init () {tot=0,ms(tag,0);} 32 inline void build (int &u,int l,int r) { 33 w[u=++tot]=mp(0,l); 34 if (l==r) return; 35 build(lc[u],l,mid),build(rc[u],mid+1,r); 36 } 37 inline void insert (int &u,int v,LL z) { 38 tag[u=++tot]=tag[v]+z; 39 lc[u]=lc[v],rc[u]=rc[v],w[u]=w[v],w[u].first+=z; 40 } 41 inline void upload (int u) { 42 w[u]=max(w[lc[u]],w[rc[u]]); 43 } 44 inline void download (int &u) { 45 insert(lc[u],lc[u],tag[u]),insert(rc[u],rc[u],tag[u]); 46 tag[u]=0; 47 } 48 inline void modify (int &u,int v,int l,int r,int x,int y,LL z) { 49 if (x<=l&&r<=y) {insert(u,v,z); return;} 50 if (tag[u]) download(v); 51 lc[u=++tot]=lc[v],rc[u]=rc[v],w[u]=w[v]; 52 if (x<=mid) modify(lc[u],lc[v],l,mid,x,y,z); 53 if (y>mid) modify(rc[u],rc[v],mid+1,r,x,y,z); 54 upload(u); 55 } 56 inline pli query (int u,int l,int r,int x,int y,pli ret=mp(-1e18,0)) { 57 if (x<=l&&r<=y) return w[u]; 58 if (tag[u]) download(u); 59 if (x<=mid) ret=query(lc[u],l,mid,x,y); 60 if (y>mid) ret=max(ret,query(rc[u],mid+1,r,x,y)); 61 return ret; 62 } 63 } using namespace TREE; 64 65 inline void extend (int x,int l,int r) { 66 if (l>r) return; 67 pli nxt=query(x,1,n,l,r); 68 q.push(node(nxt.first,x,l,r,nxt.second)); 69 } 70 71 int main () { 72 scanf("%d%d",&n,&m),init(),build(rt[0],1,n); 73 for (int i=1,x; i<=n; ++i) { 74 scanf("%d",&x); 75 modify(rt[i],rt[i-1],1,n,pre[x]+1,i,(LL)x); 76 extend(rt[i],1,i),pre[x]=i; 77 } 78 node cur; 79 for ( ; m; --m) { 80 cur=q.top(),q.pop(); 81 extend(cur.x,cur.l,cur.p-1); 82 extend(cur.x,cur.p+1,cur.r); 83 } 84 printf("%lld\n",cur.v); 85 return 0; 86 }
原文地址:https://www.cnblogs.com/whc200305/p/8584871.html