1. 程式人生 > >整體二分--poj2104靜態區間第k小

整體二分--poj2104靜態區間第k小

傳送門

 昨天跟yousiki學到了整體二分!quq

最經典的就是靜態區間第k小(大)

聽說2013許昊然的論文-《淺談資料結構題的幾個非經典解法》 講的特別好,從網上找了個圖


複雜度很優秀的說!

具體做法一般是:

1、找到詢問答案的上限和下限limit,開始二分

2、每次二分出一個mid,判斷答案在l~mid之間的詢問,如果有修改就判斷修改的影響是否在l~mid之間,然後將l到r內的操作分成兩部分,左邊是l~mid之間的,右邊是mid+1~r之間的

3、分別遞迴求解兩部分的答案

4、如果二分的val值vl==vr,l~r內詢問的答案已經確定,為vl,return

5、判斷部分一般需要資料結構支援

這道題的做法也差不多啦,資料結構支援單點加和區間求和,用樹狀陣列就可以

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define M 50005
#define N 100005
using namespace std;
int n,m,f[N],mx,ans[M];

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') {if(c=='-')f=-1;c=getchar();}
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}

struct qwq{
	int val,num;
}a[N];
inline bool cmp(qwq x,qwq y){return x.val<y.val;}
struct Query{
	int cnt,k,num,l,r;
}q[M],b[M];

inline void add(int x,int y){
	for(;x<=n;x+=x&-x)
		f[x]+=y;
}

inline int query(int x){
	int sum=0;
	for(;x;x-=x&-x)
		sum+=f[x];
	return sum;
}

inline void calc(int vl,int vr,int l,int r){
	int ll=1,rr=n+1;
	while(ll<rr){//找到a陣列中大於等於vl的最後一個 
		int mid=(ll+rr)>>1;
		if(a[mid].val>=vl) rr=mid;
		else ll=mid+1;
	}
	for(int i=rr;i<=n && a[i].val<=vr;i++)
		add(a[i].num,1);//將小於等於mid的加入 
	for(int i=l;i<=r;i++)
		q[i].cnt=query(q[i].r)-query(q[i].l-1);//計算詢問在l~mid的答案 
	for(int i=rr;i<=n && a[i].val<=vr;i++)//記得還原 
		add(a[i].num,-1);
}

inline void solve(int l,int r,int vl,int vr){
	if(vl==vr){
		for(int i=l;i<=r;i++)
			ans[q[i].num]=vl;
		return;
	}
	int mid=(vl+vr)>>1;
	calc(vl,mid,l,r);//計算詢問中 
	int now1=l,now2=r;
	for(int i=l;i<=r;i++)//將詢問分成兩部分 
		if(q[i].cnt>=q[i].k) b[now1++]=q[i];
		else q[i].k-=q[i].cnt,b[now2--]=q[i];
	for(int i=l;i<=r;i++) q[i]=b[i];//合併兩部分 
	if(now1!=l) solve(l,now1-1,vl,mid);//遞迴求解 
	if(now2!=r) solve(now2+1,r,mid+1,vr);
	return;
}

int main(){
	n=rd(); m=rd();
	for(int i=1;i<=n;i++) a[i].val=rd(),a[i].num=i;
	a[n+1].val=1e9+5;
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=m;i++){
		q[i].num=i; q[i].l=rd(); q[i].r=rd(); q[i].k=rd();
	}
	solve(1,m,-1e9,1e9);
	for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
	return 0;
}

相關推薦

整體二分--poj2104靜態區間k

傳送門  昨天跟yousiki學到了整體二分!quq 最經典的就是靜態區間第k小(大) 聽說2013許昊然的論文-《淺談資料結構題的幾個非經典解法》 講的特別好,從網上找了個圖 複雜度很優秀的說! 具體做法一般是: 1、找到詢問答案的上限和下限limit,開始二

整體二分初步——靜態區間k

Description 給定一個長度為n的序列,m個詢問,每個詢問的形式為:L,r,k表示在[L,r]間中的第k大元素。 Input 第1行:2個數,n,m表示序列的長度和詢問的個數 第2行:n個數,表示n個數的大小 第3-m+2行:每行3個數,L,r,k表示詢問在[L

靜態區間K整體二分、主席樹)

題目連結 題解 主席樹入門題 但是這裡給出整體二分解法 整體二分顧名思義是把所有操作放在一起二分 想想,如果求\([1-n]\)的第\(k\)小怎麼二分求得? 我們可以二分答案\(k\), \(O(n)\)統計有多少個數小於等於\(k\) 如果對於每個詢問都這麼搞,肯定不行 我們可以發現,如果

poj2104區間k,靜態主席樹入門模板

看了很久的主席樹,最後看https://blog.csdn.net/williamsun0122/article/details/77871278這篇終於看懂了 #include <stdio.h> #include<algorithm> using namespace s

整體二分——帶修改區間k

Description   給定一個長度為N的已知序列A[i](1<=i<=N),要求維護這個序列,能夠支援以下兩種操作:   1、查詢A[i],A[i+1],A[i+2],…,A[j](1<=i<=j<=N)中,升序排列後排名第k的數。   

整體二分求動態區間k

long long unsigned char else 樹狀數組 linker truct ati pos 比樹狀數組套主席樹不知道高到哪裏去了,solve(l,r,L,R)就是對於L,R的操作區間的答案都在l,r區間裏,然後遞歸下去 復雜度O(nlognlogn),每個

【XSY2720】區間k 整體二分 可持久化線段樹

cpp markdown 區間 序列 printf line 線段 using back 題目描述   給你你個序列,每次求區間第\(k\)小的數。   本題中,如果一個數在詢問區間中出現了超過\(w\)次,那麽就把這個數視為\(n\)。   強制在線。   \(n\leq

整體二分(模板) 求區間k

整體二分,將詢問與初值一起放入一個結構體裡,然後每次二分判斷詢問在哪邊,樹狀陣列維護,時間複雜度O((n+Q)lognlogMAX_a[i] 程式碼 #include<iostream

[poj 2104]主席樹+靜態區間k

include end 區間 得到 name int 題目 tar tdi 題目鏈接:http://poj.org/problem?id=2104 主席樹入門題目,主席樹其實就是可持久化權值線段樹,rt[i]維護了前i個數中第i大(小)的數出現次數的信息,通過查詢兩棵樹的差

poj 2104主席樹求區間k

區間 ++ cto ast http lan air algorithm while POJ - 2104 題意:求區間第k小 思路:無修改主席樹 AC代碼: #include "iostream" #include "iomanip" #include "string.

少年,想學帶修改主席樹嗎 | BZOJ1901 帶修改區間k

== write algo i++ sin esp 天下 read 一個 少年,想學帶修改主席樹嗎 | BZOJ1901 帶修改區間第k小 有一道題(BZOJ 1901)是這樣的:n個數,m個詢問,詢問有兩種:修改某個數/詢問區間第k小。 不帶修改的區間第k小用主席樹很好

靜態區間k大 樹套樹解法

i++ val pri math 1+n std -a 平衡樹 scanf 然而過不去你谷的模板 思路: 值域線段樹\([l,r]\)代表一棵值域在\([l,r]\)範圍內的點構成的一顆平衡樹 平衡樹的\(BST\)權值為點在序列中的位置 查詢區間第\(k\)大值時 左區間

HDU 2665.Kth number-無修改區間K-可持久化線段樹(主席樹)模板

sort ota nbsp ani show 去重 第k小 math urn Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Oth

HDU 5919 - Sequence II (2016CCPC長春) 主席樹 (區間K+區間不同值個數)

HDU 5919 題意:   動態處理一個序列的區間問題,對於一個給定序列,每次輸入區間的左端點和右端點,輸出這個區間中:每個數字第一次出現的位子留下, 輸出這些位子中最中間的那個,就是(len+1)/2那個。 思路:   主席樹操作,這裡的思路是從n到1開始建樹。其他就是主席樹查詢區間第K小,計算區

落谷 P3834 可持久化線段樹 1(主席樹)(區間k

設區間為l,r,用r版本減去l版本求出區間第k小,一個板子 #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using

區間k(主席樹)

區間第k小 題目描述 如題,給定NNN個正整數構成的序列,將對於指定的閉區間查詢其區間內的第KKK小值。 輸入格式 第一行包含兩個正整數NNN、MMM,分別表示序列的長度和查詢的個數。 第二行包含NN

可持久化線段樹——Step 1 靜態區間K

考慮這樣一個問題: 給出一段長度為n序列{ai},對於一些詢問{L,R,K}請輸出序列上[L,R]內第K大的數。 關於暴力做法,其實是很簡單的,但是會超時,在此略過。 有一種辦法,是利用字首和的思想。先將{ai}離散到區間[1,n],然後,對於任意節點i,

主席樹入門詳解一(學習筆記)(例題POJ-2104 求區間k

學習主席樹,在網上搜了很多教程(都好簡短啊,直接就是幾行字就上程式碼,看不懂啊有木有~~),最後才很艱難的學會了最基礎的部分。下面就是我在學習的過程中的產生的疑惑和解決的辦法。 學習主席樹需要的前置技能:線段樹。 參考資料 1. B站上的視訊講解(話說B站真的啥都有啊)

靜態區間K

<a target=_blank href="http://poj.org/problem?id=2104" target="_blank"><span style="font-s

動態區間k(主席樹+線段樹套樹狀陣列)

靜態區間第k小問題,是給你一個序列,每次詢問序列中的一個區間中的第k小數,這個問題用普通的主席樹就可以解決。動態區間第k小問題就是在靜態的基礎上加上了修改操作,也就是每次除了詢問區間第k小之外,還可以修改序列中的某個數。因為這裡涉及到了修改操作,我們用只用主席樹