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 MB
Submit: 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

Sample Output

4

HINT

 

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

 

 

posted @ 2018-06-21 00:49  wang9897  阅读(239)  评论(0编辑  收藏  举报