zoukankan      html  css  js  c++  java
  • 【BZOJ4504】K个串 可持久化线段树+堆

    【BZOJ4504】K个串

    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

    Sample Output

    4

    HINT

    1 <= n <= 100000, 1 <= k <= 200000, 0 <= |a_i| <= 10^9数据保证存在第 k 大的和

    题解:沿用超级钢琴的思路。用堆维护五元组(x,a,b,y,val)表示右端点为x,左端点在[a,b]中,最优的左端点是y,且y到x的和是val。然后每次从优先队列中取出val最大的,将其删去,在[a,y)和(y,b]中分别寻找新的y,然后将其扔回到队列中去。

    问题是如何找y呢?考虑可持久化线段树。因为每个区间都是某个前缀的后缀,所以如果当前的右端点是x,x的前驱是pre[x],我们只需要在x的线段树中将(pre[x],x]的权值都加上val[x]即可。可以用标记永久化来加速主席树的区间修改。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <map>
    #include <queue>
    using namespace std;
    const int maxn=100010;
    typedef long long ll;
    int n,m,tot;
    ll v[maxn];
    int rt[maxn],pre[maxn];
    map<ll,int> last;
    struct node
    {
    	int l,r,x,y;
    	ll v;
    	node() {}
    	node(int a,int b,int c,ll d,int e) {x=a,l=b,r=c,v=d,y=e;}
    	bool operator < (const node &a) const {return v<a.v;}
    };
    struct sag
    {
    	ll x;	int y;
    	sag() {x=-1ll<<60,y=0;}
    }s[maxn*100];
    int ls[maxn*100],rs[maxn*100];
    ll tag[maxn*100];
    priority_queue<node> q;
    sag operator + (const sag &a,const sag &b)
    {
    	return ((a.x==b.x)?(a.y>b.y):(a.x>b.x))?a:b;
    }
    void build(int l,int r,int &x)
    {
    	x=++tot,s[x].x=0,s[x].y=l;
    	if(l==r)	return ;
    	int mid=(l+r)>>1;
    	build(l,mid,ls[x]),build(mid+1,r,rs[x]);
    }
    void insert(int x,int &y,int l,int r,int a,int b,ll c)
    {
    	y=++tot,s[y]=s[x],ls[y]=ls[x],rs[y]=rs[x],tag[y]=tag[x];
    	if(a<=l&&r<=b)
    	{
    		s[y].x+=c,tag[y]+=c;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	if(a<=mid)	insert(ls[x],ls[y],l,mid,a,b,c);
    	if(b>mid)	insert(rs[x],rs[y],mid+1,r,a,b,c);
    	s[y]=s[ls[y]]+s[rs[y]];
    	s[y].x+=tag[y];
    }
    sag query(int l,int r,int x,int a,int b)
    {
    	if(a<=l&&r<=b)	return s[x];
    	int mid=(l+r)>>1;
    	sag ret;
    	ret.y=l;
    	if(a<=mid)	ret=ret+query(l,mid,ls[x],a,b);
    	if(b>mid)	ret=ret+query(mid+1,r,rs[x],a,b);
    	ret.x+=tag[x];
    	return ret;
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	n=rd(),m=rd();
    	int i,a,b,x;
    	ll y;
    	sag t;
    	node u;
    	build(1,n,rt[0]);
    	for(i=1;i<=n;i++)
    	{
    		v[i]=rd(),pre[i]=last[v[i]],last[v[i]]=i;
    		insert(rt[i-1],rt[i],1,n,pre[i]+1,i,v[i]);
    		t=query(1,n,rt[i],1,i);
    		q.push(node(i,1,i,t.x,t.y));
    	}
    	while(m--)
    	{
    		u=q.top(),q.pop();
    		x=u.x,a=u.l,b=u.r,y=u.y;
    		if(!m)
    		{
    			printf("%lld
    ",u.v);
    			return 0;
    		}
    		if(a<y)
    		{
    			t=query(1,n,rt[x],a,y-1);
    			q.push(node(x,a,y-1,t.x,t.y));
    		}
    		if(b>y)
    		{
    			t=query(1,n,rt[x],y+1,b);
    			q.push(node(x,y+1,b,t.x,t.y));
    		}
    	}
    	return 0;
    }//8 5 3 -2 1 2 2 1 3 -2
  • 相关阅读:
    深度学习面试题03:改进版梯度下降法Adagrad、RMSprop、Momentum、Adam
    深度学习面试题02:标准梯度下降法
    深度学习面试题01:导数、偏导数、方向导数、梯度的概念
    2014.09.14(西安绿点)
    直接拿来用!最火的Android开源项目(完结篇)
    直接拿来用!最火的Android开源项目(二)
    直接拿来用!最火的Android开源项目(一)
    直接拿来用!最火的iOS开源项目(三)
    直接拿来用!最火的iOS开源项目(二)
    直接拿来用!最火的iOS开源项目(一)
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7672429.html
Copyright © 2011-2022 走看看