1. 程式人生 > >BZOJ - 2957 (分塊/線段樹)

BZOJ - 2957 (分塊/線段樹)

http size target 兩種 vector while end ref typedef

題目鏈接

本質是維護斜率遞增序列。

用分塊的方法就是把序列分成sqrt(n)塊,每個塊分別用一個vector維護遞增序列。查詢的時候遍歷所有的塊,同時維護當前最大斜率,二分找到每個塊中比當前最大斜率大的那個點。修改的時候只需要修改點所在的那個塊即可。復雜度$O(m\sqrt nlogn)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 typedef double db;
 5 const int N=1e5+10;
 6 int n,m,h[N],in[N],L[N],R[N],sqrtn,n2;
7 vector<db> v[1000]; 8 9 int main() { 10 scanf("%d%d",&n,&m),sqrtn=sqrt(n+0.5); 11 for(int i=1; i<=n; ++i)L[i]=-1; 12 for(int i=1; i<=n; ++i) { 13 in[i]=i/sqrtn; 14 if(!~L[in[i]])L[in[i]]=i; 15 R[in[i]]=i; 16 n2=max(n2,in[i]);
17 } 18 while(m--) { 19 int x,y; 20 scanf("%d%d",&x,&y); 21 h[x]=y; 22 v[in[x]].clear(); 23 for(int i=L[in[x]]; i<=R[in[x]]; ++i) { 24 db t=(db)h[i]/i; 25 if(v[in[x]].size()&&v[in[x]].back()>t)continue
; 26 v[in[x]].push_back(t); 27 } 28 db mx=0; 29 int ans=0; 30 for(int i=0; i<=n2; ++i) { 31 int j=upper_bound(v[i].begin(),v[i].end(),mx)-v[i].begin(); 32 ans+=v[i].size()-j; 33 if(v[i].size())mx=max(mx,v[i].back()); 34 } 35 printf("%d\n",ans); 36 } 37 return 0; 38 }

用線段樹的方法是利用“每個區間的遞增序列長度為左區間的遞增序列長度加上右區間中比左區間最大值大的遞增序列長度”的性質來維護遞增序列長度。左區間的遞增序列長度可以直接加上,右區間的與左區間的最大值有關,姑且用$qry(mx[ls],rs)$來表示($mx[ls]$代表左區間最大值),則有區間合並公式:$cnt[u]=cnt[ls]+qry(mx[ls],rs)$。

然後就是$qry(mx[ls],rs)$的計算問題了。設$qry(x,u)$為區間u中比x大的部分的遞增序列長度,則分兩種情況討論:

1)u的左區間最大值大於x,此時u的遞增序列長度中位於右區間中的部分全部包含,因此$qry(x,u)=cnt[u]-cnt[ls]+qry(x,ls)$(註意不是$cnt[rs]+qry(x,rs)$,因為右區間中的部分序列會被左區間擋住)。

2)u的左區間最大值小於等於x,此時u的遞增序列長度中位於左區間中的部分全部不包含,因此$qry(x,u)=qry(x,rs)$。

綜上,只需要維護每個區間的最大值和遞增序列長度,即可在$O(log^2n)$的時間內完成一次修改操作,而查詢操作是$O(1)$的,因此總時間復雜度為$O(mlog^2n)$。

再一次體會到了區間分治的威力。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 typedef double db;
 5 const int N=1e5+10;
 6 int cnt[N<<2],n,m;
 7 db mx[N<<2];
 8 #define mid ((l+r)>>1)
 9 #define ls (u<<1)
10 #define rs (u<<1|1)
11 int qry(db x,int u,int l,int r) {
12     if(mx[u]<=x)return 0;
13     if(l==r)return cnt[u];
14     return mx[ls]>x?cnt[u]-cnt[ls]+qry(x,ls,l,mid):qry(x,rs,mid+1,r);
15 }
16 void pu(int u,int l,int r) {
17     mx[u]=max(mx[ls],mx[rs]);
18     cnt[u]=cnt[ls]+qry(mx[ls],rs,mid+1,r);
19 }
20 void upd(int p,db x,int u=1,int l=1,int r=n) {
21     if(l==r) {cnt[u]=1,mx[u]=x; return;}
22     p<=mid?upd(p,x,ls,l,mid):upd(p,x,rs,mid+1,r);
23     pu(u,l,r);
24 }
25 int main() {
26     scanf("%d%d",&n,&m);
27     while(m--) {
28         int x,y;
29         scanf("%d%d",&x,&y);
30         upd(x,(db)y/x);
31         printf("%d\n",cnt[1]);
32     }
33     return 0;
34 }

BZOJ - 2957 (分塊/線段樹)