20241218北京总结
扫描线
构建模型的主要思想 : 把问题换到平面上
二维数点 , 就是通过差分和扫描线将问题降维的问题
来看看题
Recommendations
不难发现问题其实是求包含这个区间的区间中 \(min_r\) 与 \(max_l\)
直接以 \(l,r\) 为轴建立坐标 , 完了就是扫描线裸题
当然 , 这个题还可以平衡树搞过 , 毕竟是求后继与前驱
#include<bits/stdc++.h>
using namespace std;
struct Node{
int x,y,id;
} a[11000000];
struct Query{
int lans,rans,len;
} q[11000000];
bool cmp1(Node f,Node s){
return f.x<s.x;
}
bool cmp2(Node f,Node s){
return f.y>s.y;
}
multiset<int> s;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;
cin>>T;
while(T--){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y;
a[i].id=i;
q[i].len=a[i].y-a[i].x+1;
}
sort(a+1,a+n+1,cmp1);
s.clear();
for(int i=1;i<=n;i++){
int lst=i;
s.insert(a[i].y);
while(a[i].x==a[i+1].x and i+1<=n){
s.insert(a[++i].y);
}
for(int j=lst;j<=i;j++){
auto it=s.lower_bound(a[j].y);
it++;
if(it==s.end()){
q[a[j].id].rans=-1;
}else{
q[a[j].id].rans=*it;
}
}
}
s.clear();
sort(a+1,a+n+1,cmp2);
for(int i=1;i<=n;i++){
int lst=i;
s.insert(a[i].x);
while(a[i].y==a[i+1].y and i+1<=n){
s.insert(a[++i].x);
}
for(int j=lst;j<=i;j++){
auto it=s.upper_bound(a[j].x);
it--;it--;
if(it==s.end()){
q[a[j].id].lans=-1;
}else{
q[a[j].id].lans=*it;
}
}
}
for(int i=1;i<=n;i++){
if(q[i].lans==-1 or q[i].rans==-1){
cout<<"0\n";
continue;
}
cout<<q[i].rans-q[i].lans+1-q[i].len<<'\n';
q[i].lans=q[i].rans=q[i].len=0;
a[i].id=a[i].x=a[i].y=0;
}
}
return 0;
}
the soldier of love
正难则反 , 我们直接把问题变为一个都不包含
考虑没有点的区间表示出来 , 记为 \([l,r]\) ,如果我们想没有点的话 \(l\leq L\) , \(r\geq R\) , 把它放到平面上
矩形显然有 \(n+1\) 个 , 复杂度 \(O(nlogn)\)
#include<bits/stdc++.h>
using namespace std;
struct Node{
int x,y,id;
} a[11000000];
struct Query{
int lans,rans,len;
} q[11000000];
bool cmp1(Node f,Node s){
return f.x<s.x;
}
bool cmp2(Node f,Node s){
return f.y>s.y;
}
multiset<int> s;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;
cin>>T;
while(T--){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y;
a[i].id=i;
q[i].len=a[i].y-a[i].x+1;
}
sort(a+1,a+n+1,cmp1);
s.clear();
for(int i=1;i<=n;i++){
int lst=i;
s.insert(a[i].y);
while(a[i].x==a[i+1].x and i+1<=n){
s.insert(a[++i].y);
}
for(int j=lst;j<=i;j++){
auto it=s.lower_bound(a[j].y);
it++;
if(it==s.end()){
q[a[j].id].rans=-1;
}else{
q[a[j].id].rans=*it;
}
}
}
s.clear();
sort(a+1,a+n+1,cmp2);
for(int i=1;i<=n;i++){
int lst=i;
s.insert(a[i].x);
while(a[i].y==a[i+1].y and i+1<=n){
s.insert(a[++i].x);
}
for(int j=lst;j<=i;j++){
auto it=s.upper_bound(a[j].x);
it--;it--;
if(it==s.end()){
q[a[j].id].lans=-1;
}else{
q[a[j].id].lans=*it;
}
}
}
for(int i=1;i<=n;i++){
if(q[i].lans==-1 or q[i].rans==-1){
cout<<"0\n";
continue;
}
cout<<q[i].rans-q[i].lans+1-q[i].len<<'\n';
q[i].lans=q[i].rans=q[i].len=0;
a[i].id=a[i].x=a[i].y=0;
}
}
return 0;
}
UOJ637 数据结构
同样正难则反 , 考虑每个数什么时候才不会被算入贡献 .
就是所有该数都被加一 , 所有该数减一都没被区间包含
也就是说对于 \(x\) , \(l\leq min_x\) \(and\) \(r\geq max_x\)
并且对于不含 \(x-1\) 的区间 \([L,R]\) , \(l\leq L\) , \(r\geq R\)
我们发现第一个限制条件同第一道题 , 第二个同第二道题 , 直接维护就行
#include<bits/stdc++.h>
using namespace std;
struct Node{
int x,y;
} a[1100000];
int n,m;
struct BitArray{
int f[1100000];
void init(){
memset(f,0,sizeof(f));
}
int lowbit(int x){
return x&(-x);
}
void modify(int x,int val){
//cout<<x<<"\n";
while(x<=n){
f[x]+=val;
x=x+lowbit(x);
}
}
int query(int x){
int res=0;
while(x){
res=res+f[x];
x=x-lowbit(x);
}
return res;
}
}BA;
int ans[1100000];
vector<int> g[1100000];
int val[1100000];
int maxx[1100000];
int cnt=0;
int lst[1100000];
bool vis[1100000];
int nxt[1100000];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>val[i];
}
for(int i=n;i>=1;i--){
if(lst[val[i]]){
nxt[i]=lst[val[i]];
}else{
maxx[val[i]]=i;
nxt[i]=n+1;
}
lst[val[i]]=i;
}
for(int i=0;i<=n;i++){
if(not lst[i]){
lst[i]=n+1;
}
}
for(int i=1;i<=n+1;i++){
if(maxx[i]){
BA.modify(1,1);
BA.modify(maxx[i],-1);
}
BA.modify(max(lst[i-1],maxx[i]),1);
BA.modify(n+1,-1);
}
for(int i=1;i<=m;i++){
cin>>a[i].x>>a[i].y;
g[a[i].x].push_back(i);
}
for(int i=1;i<=n;i++){
for(int j:g[i]){
ans[j]=BA.query(a[j].y);
}
lst[val[i]]=nxt[i];
if(!vis[val[i]]){
vis[val[i]]=true;
if(maxx[val[i]]<lst[val[i]-1]){
BA.modify(maxx[val[i]],1);
BA.modify(lst[val[i]-1],-1);
}
}
if(!vis[val[i]+1]){
BA.modify(max(i,maxx[val[i]+1]),-1);
BA.modify(n+1,+1);
BA.modify(max(lst[val[i]],maxx[val[i]+1]),1);
BA.modify(n+1,-1);
}
}
for(int i=1;i<=m;i++){
cout<<ans[i]<<"\n";
}
return 0;
}
函数复合
这是一类问题的 \(trick\) (?) , 具体而言 , 每次询问给定一个区间 \([l,r]\) 和初始值 \(x\) ,求 \(F_r(...F_l(x))\)
接下来我们考虑 插入-标记-回收算法 , 我个人理解上就是把询问离线后上扫描线 , 用数据结构维护函数值 , \(l\) 插入 , \(r\) 提取出来
标记永久化
不会树套树 , 待学



浙公网安备 33010602011771号