1. 程式人生 > >Bzoj4504: K個串

Bzoj4504: K個串

AD back scrip 序列 pda hint type cond discuss

題解: 我們考慮到查詢區間類不同數目的個數在線做法是主席樹維護,然後我們可以維護出每個位置的值產生貢獻的範圍,然後相當於主席樹維護了以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 大的和

Bzoj4504: K個串