Caiwen的博客

离线算法

2025-02-08 04:04:18

整体二分

整体二分的基本思想是,二分出一个 midmid,然后遍历所有的询问,每个询问都 check 一下 mid 是偏大了还是偏小了。把偏大的询问分成一组,偏小的询问分成另一组。然后这两组询问再分别二分

P3332 [ZJOI2013] K大数查询

https://www.luogu.com.cn/problem/P3332

题目描述

你需要维护 nn 个可重整数集,集合的编号从 11nn

这些集合初始都是空集,有 mm 个操作:

  • 1 l r c:表示将 cc 加入到编号在 [l,r][l,r] 内的集合中
  • 2 l r c:表示查询编号在 [l,r][l,r] 内的集合的并集中,第 cc 大的数是多少。

注意可重集的并是不去除重复元素的,如 {1,1,4}{5,1,4}={1,1,4,5,1,4}\{1,1,4\}\cup\{5,1,4\}=\{1,1,4,5,1,4\}

数据范围

1n,m5×1041 \le n,m \le 5\times 10^4

1l,rn1\le l,r \le n

11 操作中 cn|c|\le n

22 操作中 1c<2631\le c < 2^{63},第 cc 大的数存在

笔记

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define ls(k) (k)<<1 #define rs(k) (k)<<1|1 #define debug(x) cout<<#x<<"="<<x<<endl using namespace std; const int inf=0x3f3f3f3f3f3f3f3f; const int mod=0; typedef pair<int,int> pii; #define _ 50004 int n,arr[_],m,sum[_<<2],tag[_<<2]; inline void pushup(int k){sum[k]=sum[ls(k)]+sum[rs(k)];} inline void pushadd(int k,int l,int r,int v){sum[k]+=v*(r-l+1),tag[k]+=v;} inline void pushdown(int k,int l,int r,int mid){if(tag[k])pushadd(ls(k),l,mid,tag[k]),pushadd(rs(k),mid+1,r,tag[k]),tag[k]=0;} void modify(int k,int l,int r,int x,int y,int v){ if(l>=x&&r<=y) return pushadd(k,l,r,v),void(); int mid=(l+r)>>1; pushdown(k,l,r,mid); if(x<=mid) modify(ls(k),l,mid,x,y,v); if(y>mid) modify(rs(k),mid+1,r,x,y,v); pushup(k); } int query(int k,int l,int r,int x,int y){ if(l>=x&&r<=y) return sum[k]; int mid=(l+r)>>1,res=0; pushdown(k,l,r,mid); if(x<=mid) res+=query(ls(k),l,mid,x,y); if(y>mid) res+=query(rs(k),mid+1,r,x,y); return res; } struct Q{int l,r,x,id,ans,op;} L[_],R[_],q[_]; void solve(int l,int r,int ql,int qr){ if(ql>qr) return; if(l==r){ for(int i=ql;i<=qr;i++) if(q[i].op==2) q[i].ans=l; return; } int mid=(l+r)>>1; int cntl=0,cntr=0; for(int i=ql;i<=qr;i++){ if(q[i].op==1){ if(q[i].x>mid) modify(1,1,n,q[i].l,q[i].r,1),R[++cntr]=q[i]; else L[++cntl]=q[i]; }else{ int cnt=query(1,1,n,q[i].l,q[i].r); if(q[i].x<=cnt) R[++cntr]=q[i]; else q[i].x-=cnt,L[++cntl]=q[i]; } } for(int i=ql;i<=qr;i++) if(q[i].op==1&&q[i].x>mid) modify(1,1,n,q[i].l,q[i].r,-1); for(int i=1;i<=cntl;i++) q[ql-1+i]=L[i]; for(int i=1;i<=cntr;i++) q[ql+cntl-1+i]=R[i]; solve(l,mid,ql,ql+cntl-1); solve(mid+1,r,ql+cntl,qr); } inline void subtask(){ cin>>n>>m; for(int i=1;i<=m;i++) cin>>q[i].op>>q[i].l>>q[i].r>>q[i].x,q[i].id=i; solve(-n,n,1,m); sort(q+1,q+m+1,[](Q x,Q y){return x.id<y.id;}); for(int i=1;i<=m;i++) if(q[i].op==2) cout<<q[i].ans<<endl; } signed main(){ ios::sync_with_stdio(false); int t=1;//cin>>t; while(t--) subtask(); return 0; }

因为我们判断 mid 是偏大了还是偏小了,需要知道比 mid 大的数有多少,因此我们把大于 midmidxx 值加入线段树。并且,如果询问需要归到 RR 中,那么也只有 xx 值大于 midmid 的操作一会影响到这些询问,因此 xx 值大于 midmid 的操作也归到 RR

P3527 [POI 2011] MET-Meteors

https://www.luogu.com.cn/problem/P3527

题目描述

nn 个成员国。现在它发现了一颗新的星球,这颗星球的轨道被分为 mm 份(第 mm 份和第 11 份相邻),第 ii 份上有第 aia_i 个国家的太空站。

这个星球经常会下陨石雨。BIU 已经预测了接下来 kk 场陨石雨的情况。

BIU 的第 ii 个成员国希望能够收集 pip_i 单位的陨石样本。你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石。

输入格式

第一行是两个数 n,mn,m

第二行有 mm 个数,第 ii 个数 oio_i 表示第 ii 段轨道上有第 oio_i 个国家的太空站。

第三行有 nn 个数,第 ii 个数 pip_i 表示第 ii 个国家希望收集的陨石数量。

第四行有一个数 kk,表示 BIU 预测了接下来的 kk 场陨石雨。 接下来 kk 行,每行有三个数 li,ri,ail_i,r_i,a_i ,表示第 kk 场陨石雨的发生地点在从 lil_i 顺时针到 rir_i 的区间中(如果 liril_i \leq r_i,则是 li,li+1,ril_i, l_i + 1 \cdots, r_i,否则就是 li,li+1,m1,m,1,2,ril_i, l_i + 1, \cdots m - 1, m, 1, 2, \cdots r_i),向区间中的每个太空站提供 aia_i 单位的陨石样本。

数据范围

1n,m,k31051\le n,m,k\le 3\cdot10^5

1pi,ai1091\le p_i,a_i\le 10^9

笔记

把环形的区间加拆成两个操作,然后整体二分即可。整体二分时直接暴力计算每个询问是否满足要求貌似是可以的

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define ls(k) (k)<<1 #define rs(k) (k)<<1|1 #define debug(x) cout<<#x<<"="<<x<<endl using namespace std; const int inf=0x3f3f3f3f3f3f3f3f; const int mod=0; typedef pair<int,int> pii; #define _ 900005 int arr[_],n,m; inline int lowbit(int x){return x&-x;} inline void modify(int x,int v){while(x<=m) arr[x]+=v,x+=lowbit(x);} inline int query(int x,int res=0){while(x) res+=arr[x],x-=lowbit(x);return res;} struct Q{int l,r,id,ans,op,x,need;} q[_],L[_],R[_]; vector<int> g[_]; void solve(int l,int r,int ql,int qr){ if(ql>qr) return; if(l==r){ for(int i=ql;i<=qr;i++) if(q[i].op==0) q[i].ans=l; return; } int mid=(l+r)>>1,cntl=0,cntr=0; for(int i=ql;i<=qr;i++){ if(q[i].op==1){ if(q[i].id<=mid) modify(q[i].l,q[i].x),modify(q[i].r+1,-q[i].x),L[++cntl]=q[i]; else R[++cntr]=q[i]; }else{ int s=0; for(auto to:g[q[i].x]){ s+=query(to); if(s>=q[i].need) break; } if(s>=q[i].need) L[++cntl]=q[i]; else q[i].need-=s,R[++cntr]=q[i]; } } for(int i=ql;i<=qr;i++) if(q[i].op==1&&q[i].id<=mid) modify(q[i].l,-q[i].x),modify(q[i].r+1,q[i].x); for(int i=1;i<=cntl;i++) q[ql+i-1]=L[i]; for(int i=1;i<=cntr;i++) q[ql+cntl+i-1]=R[i]; solve(l,mid,ql,ql+cntl-1); solve(mid+1,r,ql+cntl,qr); } int need[_]; inline void subtask(){ cin>>n>>m; for(int i=1,x;i<=m;i++) cin>>x,g[x].push_back(i); for(int i=1;i<=n;i++) cin>>need[i]; int totq,cntq=0;cin>>totq; for(int i=1;i<=totq;i++){ int l,r,x;cin>>l>>r>>x; if(l<=r) q[++cntq].l=l,q[cntq].r=r,q[cntq].op=1,q[cntq].id=i,q[cntq].x=x; else q[++cntq].l=1,q[cntq].r=r,q[cntq].op=1,q[cntq].id=i,q[cntq].x=x,q[++cntq].l=l,q[cntq].r=m,q[cntq].op=1,q[cntq].id=i,q[cntq].x=x; } for(int i=1;i<=n;i++) q[++cntq].id=i,q[cntq].x=i,q[cntq].need=need[i]; //for(int i=1;i<=cntq;i++) debug(q[i].op),debug(q[i].l),debug(q[i].r); solve(1,totq+1,1,cntq); sort(q+1,q+cntq+1,[](Q x,Q y){return x.id<y.id;}); for(int i=1;i<=cntq;i++){ if(q[i].op) continue; if(q[i].ans==totq+1) cout<<"NIE"<<endl; else cout<<q[i].ans<<endl; } } signed main(){ ios::sync_with_stdio(false); int t=1;//cin>>t; while(t--) subtask(); return 0; }
最后更新于:2025-04-03 12:11:09

Caiwen
本文作者
一只蒟蒻,爱好编程和算法