线段树模板二
1:扫描线+树状数组
扫描线排序:
按先y轴从小到大在按0,1
二维数点
题意
平面上有n个点(xi,yi)。回答q个询问,每个询问给定一个矩形[X1,X2]×[Y1,Y2],询问矩形里面有多少个点。
输入格式
第一行两个整数n,q(1≤n,q≤2×105)。
接下来n行,每行两个整数xi,yi(1≤xi,yi≤109)。
接下来q行,每行四个整数X1,X2,Y1,Y2(1≤X1≤X2≤109,1≤Y1≤Y2≤109)。
输出格式
对于每组询问,输出一个数表示答案。
样例输入
5 5
1 3
2 5
3 2
4 4
5 1
1 5 1 5
2 4 3 5
1 3 2 5
100 100 100 100
1 1 3 3
样例输出
5
2
3
0
1
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
int n,m,k;
struct now{
int a;
int b;
int c;
int d;
};
vector<now>seg;
bool cmp(now l, now r)//排序注意点
{
if(l.a==r.a)
return l.c < r.c;
return l.a < r.a;
}
int num[N],cnt[N];
void modify(int l,int r)
{
for(;l <= k;)
{
num[l]+=r;
l += (l&-l);
}
}
int query(int l)
{
int r = 0;
for(;l;)
{
r += num[l];
l -= (l&-l);
}
return r;
}
void solve()
{
cin >> n >> m;
vector<int> ans;
for(int i = 1;i <= n;i ++)
{
int x,y;
cin >> x >> y;
seg.push_back({y,x,0,0});
ans.push_back(x);
}
for(int i = 1;i <= m;i ++)
{
int x,y,xx,yy;
cin >> x >> xx >> y >> yy;
seg.push_back({yy,xx,1,i});
seg.push_back({y-1,x-1,1,i});
seg.push_back({y-1,xx,2,i});
seg.push_back({yy,x-1,2,i});
}
sort(seg.begin(),seg.end(),cmp);
sort(ans.begin(),ans.end());
ans.erase(unique(ans.begin(),ans.end()),ans.end());
k = seg.size();
for(int i = 0;i < seg.size();i ++)
{
if(seg[i].c == 0)
{
int pos = lower_bound(ans.begin(),ans.end(),seg[i].b)-ans.begin()+1;
modify(pos,1);//统计前缀和
}
else
{
int pos = upper_bound(ans.begin(),ans.end(),seg[i].b)-ans.begin();
int res = query(pos);
if(seg[i].c==1)
cnt[seg[i].d]+=res;
else
cnt[seg[i].d]-=res;
}
}
for(int i = 1;i <= n;i ++)
cout << cnt[i] << '\n';
}
signed main()
{
int tt = 1;
//sc(tt);
while(tt--)
{
solve();
}
}
区间不同数之和
题意
有n个数a1,a2,…,an。有q组询问,每次给一个区间[l,r],求区间里不同的数字之和,也就是说同一个数字出现多次只算一次。
输入格式
第一行两个数n,q(1≤n,q≤2×1e5)。
接下来一行,一共n个数a1,a2,…,an(1≤ai≤n)。
接下来q行,每行两个整数l,r(1≤l≤r≤n)。
输出格式
对于每个询问,每行输出一个数表示答案。
样例输入
5 5
2 2 3 2 1
1 5
2 3
2 4
1 2
3 5
输出格式
6
5
5
2
6
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int n,m;
int a[N],pre[N],last[N],ans[N],f[N];
struct now{
int x;
int val;
int lei;
int poi;
};
vector<now>seg;
bool cmp(now c,now b)
{
if(c.x == b.x)
return c.lei < b.lei;
return c.x < b.x;
}
void add(int u,int d)
{
for(;u<= N;)
{
f[u]+=d;
u += (u&-u);
}
}
int query(int u)
{
int num = 0;
for(;u;)
{
num += f[u];
u -= (u&-u);
}
return num;
}
void solve()
{
cin >> n >> m;
for(int i = 1;i <= n;i ++)
cin >> a[i];
for(int i = 1;i <= n;i ++)
{
pre[i] = last[a[i]];
last[a[i]] = i;
seg.push_back({pre[i],i,0,a[i]});
}
for(int i = 1;i <= m;i ++)
{
int l,r;
cin >> l >> r;
seg.push_back({l-1,l-1,1,i});
seg.push_back({l-1,r,2,i});
}
sort(seg.begin(),seg.end(),cmp);
for(auto c:seg)
{
if(c.lei == 0)
{
add(c.val,c.poi);
}
else
{
if(c.lei == 1)
ans[c.poi] -= query(c.val);
else
ans[c.poi] += query(c.val);
}
}
for(int i = 1;i <= m;i ++)
cout << ans[i] << '\n';
}
signed main()
{
solve();
}
2:字典树,权值线段树
异或第k小
给n个数字a1,a2,…,an。你要回答m个询问,每次给定两个数x,k,询问a1 xor x,a2 xor x,…,an xor x中从小到大排序中第k小的元素。
输入格式
第一行包含两个整数n,m。
第二行包含n个整数a1,a2,…,an。
接下来m行,每行两个数x,k(1≤k≤n),表示一组询问。
输出格式
一共m行,每行一个整数,表示上面数字里面的第k小。
### 样例输入
5 5
11 10 6 4 14
15 1
11 4
3 5
6 1
9 1
样例输出
1
13
13
0
2
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
int n,m;
const int M = 30;
struct now{
int w[3];
int sz;
}seg[N*4];
int tot = 0,root;
void solve()
{
cin >> n >> m;
root = ++tot;
for(int i = 1;i <= n;i++)
{
int x;
cin >> x;
int p = root;
for(int j = M-1;j >= 0;j --)
{
seg[p].sz++;
int u = (x>>j)&1;
if(seg[p].w[u] == 0)
seg[p].w[u] = ++tot;
p = seg[p].w[u];
}
seg[p].sz++;
}
for(int i = 1;i <= m;i ++)
{
int x,y;
cin >> x >> y;
int p = root;
int ans = 0;
for(int j = M-1;j >= 0;j --)
{
int u = (x>>j)&1;
if(seg[seg[p].w[u]].sz>=y)
{
p = seg[p].w[u];
}
else
y -= seg[seg[p].w[u]].sz,ans^=(1<<j),p = seg[p].w[u^1];
}
cout << ans << '\n';
}
}
signed main()
{
int tt = 1;
while(tt--)
{
solve();
}
}
3:离线+线段树
mex
有一个长度为n的数组 a1,a2,…,an。
你要回答q个询问,每次给一个区间[l,r],询问这个区间内最小没有出现过的自然数。
输入格式
第一行两个数n,q(1≤n,q≤2×105)。
接下来一行,一共n个数a1,a2,…,an(0≤ai≤109)。
接下来q行,每行两个整数l,r(1≤l≤r≤n)。
输出格式
对于每个询问,每行输出一个数表示答案。
样例输入
5 5
2 1 0 2 1
3 3
2 3
2 4
1 2
3 5
输出格式
1
2
3
0
3
点击查看代码
#include<bits/stdc++.h>
#define endl '\n'
#define IO ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using namespace std;
typedef pair<int,int> pii;
using ll = long long;
// 维护 第一个 最后出现的位置 小于l的
const int N = 2e5+5;
int tr[4*N],ans[N],pos[N],n,q,t,v[N];
vector<pii> que[N];
int op(int l,int r){
return min(tr[l],tr[r]);
}
void change(int u,int l,int r,int pos,int val){
if(l==r){
tr[u]=val;
return ;
}
int mid=l+r>>1;
if(pos<=mid) change(u*2,l,mid,pos,val);
else change(u*2+1,mid+1,r,pos,val);
tr[u]=op(2*u,2*u+1);
}
int query(int u,int l,int r,int val){
if(l==r) return l;
int mid=l+r>>1;
if(tr[2*u]<val) return query(u*2,l,mid,val);
else return query(u*2+1,mid+1,r,val);
}
signed main() {
IO;
cin >> n>>q;
for (int i = 1; i <= n; i ++ ){
cin >> v[i];
v[i]=min(v[i],n+1);
}
int l,r;
for (int i = 0; i < q; i ++ ){
cin >> l>>r;
que[r].push_back({l,i});
}
for (int i = 1; i <= n; i ++ ){
change(1,0,n+1,v[i],i);
for(auto &[a,b]:que[i]){
ans[b]=query(1,0,n+1,a);
}
}
for (int i = 0; i < q; i ++ )
cout << ans[i]<<endl;
return 0;
}