description
有n個國家和m個空間站,每個空間站都屬於乙個國家,乙個國家可以有多個空間站,所有空間站按照順序形成乙個環,也就是說,m號空間站和1號空間站相鄰。現在,將會有k場流星雨降臨,每一場流星雨都會給區間[li,ri]內的每個空間站帶來ai單位的隕石,每個國家都有乙個收集隕石的目標pi,即第i個國家需要收集pi單位的隕石。
詢問:每個國家最早完成隕石收集目標是在第幾場流星雨後,如果所有流星雨結束後某個國家還未完成收集目標則輸出nie
input
第一行為兩個整數n和m表示國家數和空間站數,第二行m個整數oi表示每個空間站所屬國家,第三行n個整數表示每個國家的收集目標need,第四行為一整數k表示流星雨次數,最後k行每行三個整數li,ri,ai表示該場流星雨都會給區間[li,ri]內的每個空間站帶來ai單位的隕石
(1<=n<=m<=300000,1<=oi<=n,1<=need<=10^9,1<=ai<=10^9)
output
獨處每個國家最早完成隕石收集目標是在第幾場流星雨,如果所有流星雨結束後某個國家還未完成收集目標則輸出nie
sample input
3 5
1 3 2 1 3
10 5 7
3 4 2 4
1 3 1
3 5 2
sample output
3 nie
1solution
整體二分
對於單個查詢(假設為第i個國家),我們可以二分k,每次對於乙個區間[l,r],手動模擬一下在第mid場流星雨過後,第i個國家一共收集到了多少單位的隕石,如果比pi大,那麼答案在[l,mid]範圍內,否則答案在[mid+1,r]範圍內。
對於多組查詢,我們也可以這麼做。首先,我們需要用乙個列表id記錄所有查詢的編號,剛開始的時候,id自然是遞增的.同時,我們用乙個陣列cur[i]記錄下,第i個國家在l-1場流星雨過後,收集到的隕石的數目。
主過程為divide(int head,int tail,int l,int r),表示對於id[head]到id[tail]的所有詢問,在[l,r]範圍內查詢答案,通過上一層的操作,我們保證id[head]到id[tail]的所有詢問的答案都在[l,r]範圍內。
首先,我們先模擬[l,mid]這麼多次操作(在詢問重新劃分之後,必須要再次模擬,將陣列清空),用樹狀陣列或者是線段樹計算出在[l,mid]場流星雨之後,每個空間站收集到的隕石的數目。
然後我們查詢,每個國家收集到的隕石的數目,要注意的是,我們需要用鍊錶儲存每個國家對應的空間站,並且一一枚舉,用tmp[id[i]]表示國家id[i]收集到的隕石的數目。
那麼從[1,mid]這麼多次操作之後,國家id[i]收集到的隕石數目就是temp[id[i]]+cur[id[i]],如果temp[id[i]]+cur[id[i]]>p[id[i]],那麼表明對於國家id[i],其答案在[l,mid]這個範圍內,否則其答案在[mid+1,r]範圍內,並將temp[id[i]]累加到cur[id[i]]上。
具體操作看**~
code
#include
#include
#include
using namespace std;
typedef long long ll;
#define maxn 333333
#define inf 1000000000ll
struct query
q[maxn],q1[maxn],q2[maxn];
struct node
p[maxn];
int n,m,k,num,cnt;
int fir[maxn],next[maxn],ans[maxn];
ll b[maxn];
void init()
int lowbit(int
x)void update(int
x,ll v)
ll sum(int
x)void adjust(int l,int r,ll v)
void divide(int head,int tail,int l,int r)
int mid=(l+r)>>1,res1=0,res2=0;
for(int i=l;i<=mid;i++)
if(p[i].l<=p[i].r)adjust(p[i].l,p[i].r,p[i].a);
else adjust(p[i].l,m,p[i].a),adjust(1,p[i].r,p[i].a);
for(int i=head;i<=tail;i++)
for(int i=l;i<=mid;i++)
if(p[i].l<=p[i].r)adjust(p[i].l,p[i].r,-p[i].a);
else adjust(p[i].l,m,-p[i].a),adjust(1,p[i].r,-p[i].a);
for(int i=1;i<=res1;i++)q[head+i-1]=q1[i];
for(int i=1;i<=res2;i++)q[head+res1+i-1]=q2[i];
divide(head,head+res1-1,l,mid);
divide(head+res1,tail,mid+1,r);
}int main()
for(int i=1;i<=n;i++)
scanf("%d",&k);
for(int i=1;i<=k;i++)
scanf("%d
%d%d",&p[i].l,&p[i].r,&p[i].a);
p[++k].l=1,p[k].r=m,p[k].a=inf;
divide(1,n,1,k);
for(int i=1;i<=n;i++)
if(ans[i]==k)printf("nie\n");
else
printf("%d\n",ans[i]);
}return
0;}
BZOJ 2738 矩陣乘法 整體二分
題目大意 給出乙個n n的矩陣,每次詢問乙個子矩形的第k小數。整體二分即在二分答案的同時把詢問分到兩個集合中再分別二分直到每個詢問出解。相當於把多個詢問一起二分 如何判斷乙個數x是不是k小呢 把矩陣中的小於等於x的數設為1,大於x的數設為0,求詢問矩陣中的小於等於x的數有多少個,大於等於k說明x有可...
BZOJ 2738 矩陣乘法 整體二分
給你乙個n n的矩陣,不用算矩陣乘法,但是每次詢問乙個子矩形的第k小數。愚蠢的名字.整體二分,影響因子就是矩陣裡的數 把 le mid 的矩陣元素加到二維樹狀陣列裡然後詢問分成兩組就行了 可以把矩陣元素權值排序後直接二分矩陣元素而不是值 複雜度 o nlog 3n 用排序代替一維樹狀陣列理論上更快,...
bzoj2738 矩陣乘法 整體二分
題意 求出子矩陣 k 小值。果然除了最小割題面給什麼演算法不能用什麼演算法 實際上區間 k 大值離線通解大概就是整體二分吧 二分最大值和最小值,動態維護,如果這個區間有這麼多個小於它的數,就設為 mid 接下來繼續遞迴修改。1 include 2 include 3 include 4 includ...