园丁的烦恼|P2163
https://www.luogu.com.cn/problem/P2163
题目背景
很久很久以前,在遥远的大陆上有一个美丽的国家。统治着这个美丽国家的国王是一个园艺爱好者,在他的皇家花园里种植着各种奇花异草。
有一天国王漫步在花园里,若有所思,他问一个园丁道: “最近我在思索一个问题,如果我们把花坛摆成六个六角形,那么……”
“那么本质上它是一个深度优先搜索,陛下。”园丁深深地向国王鞠了一躬。
“嗯……我听说有一种怪物叫九头蛇,它非常贪吃苹果树……”
“是的,显然这是一道经典的动态规划题,早在 N 元 400240024002 年我们就已经发现了其中的奥秘了,陛下。”
“该死的,你究竟是什么来头?”
“陛下息怒,干我们的这行经常莫名其妙地被问到和 OI 有关的题目,我也是为了预防万一啊!” 王者的尊严受到了伤害,这是不可容忍的。
题目描述
看来一般的难题是难不倒这位园丁的,国王最后打算用车轮战来消耗他的实力: “年轻人,在我的花园里有 n 棵树,每一棵树可以用一个整数坐标来表示,一会儿,我的 m 个骑士们会来轮番询问你某一个矩阵内有多少树,如果你不能立即答对,你就准备走人吧!”说完,国王气呼呼地先走了。
这下轮到园丁傻眼了,他没有准备过这样的问题。所幸的是,作为“全国园丁保护联盟”的会长——你,可以成为他的最后一根救命稻草。
输入格式
第一行有两个整数 n,m,分别表示树木个数和询问次数。
接下来 n 行,每行两个整数 x,y,表示存在一棵坐标为 (x,y)的树。有可能存在两棵树位于同一坐标。
接下来 m 行,每行四个整数 a,b,c,d,表示查询以 (a,b)为左下角,(c,d)为右上角的矩形内部(包括边界)有多少棵树。
输出格式
对于每个查询,输出一行一个整数表示答案。
输入输出样例
输入 #1
3 1
0 0
0 1
1 0
0 0 1 1
输出 #1
3
说明/提示
数据规模与约定
- 对于30\%的数据,保证 n,m≤10。
- 对于100\%的数据,保证 0≤n≤500000,1≤m≤500000,0≤x,y,a,b,c,d≤10000000,a≤c,b≤d。
两个不可行的思路:
对于每组询问,检查每棵树符不符合要求(在不在矩阵内)
复杂度:O(nm),炸了2500倍
二位前缀和,预处理后O(1)查询
这个更离谱,复杂度:O(10000000*100000000),炸了1000000倍
以上两种复杂度统称O(TLE)//都T了还算个毛线复杂度!
于是,为了解决这类问题,我们采用神奇的方法:一列一列算
(你知道没有图片多 难 。 受 )
因为观察数据发现有很多空行/空列,要是枚举或者预处理会很浪费
1 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 1
0 0 0 1 0 1 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0
0 0 1 0 0 0 0 0 0
举个栗子
如果我们要在一个一维数组里面快速求区间和,前缀和是个很好的选择
然后我们把前缀和前缀起来就是二位前缀和了(每一列的和不是自己本列的和,而是把自己和前面所有列的和加起来)
如果询问是无序的,那么我们去查询的时候的时候要吧二位前缀和的每个书都存下来,空间都炸了,别说时间,
但我们发现前缀和求法是s[i]=s[i-1]+a[i]
,它不需要前面所有列的信息
于是我们只需要询问排好序,这样就可以把数组滚动起来,空间就解决了
然后我们再看,由于500000个点在100000000000000个位置中显得真的很渺小
每次前缀和只因为几个数就要花O(10000000)大改一番,很不划算,于是我们用树状数组代替前缀和实现修改查询操作,nlogn完全可以!
虽然nlogn很好,但是最好情况我们也仍然有95\%的时间在访问空列
这很好解决,跳着访问,查询时只在非空列修改与查询
(因为空列的话代码没有运行一行要他干啥……)
#include<iostream>
#include<vector>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
//先打快读
inline int read(){
int s=0,f=1;
char c=getchar();
while(c<48 || c>57){if(c=='-')f=-f;c=getchar();}
while(c>=48 && c<=57)s=s*10+c-48,c=getchar();
return s*f;
}
//要打一棵线段树
//离散化后问题不大
const int MAXN=5e5+10;
//难蚌
int n,m;
//换成树状数组
int tc[10000001];
#define lb(x) ((x)&(-(x)))
int query(int x){
if(x==0)return 0;
int ret=0;
for(int i=x;i>0;i-=lb(i))ret+=tc[i];
return ret;
}
void modify(int x,int v){
if(x==0)return;
for(int i=x;i<=10000000;i+=lb(i))tc[i]+=v;
}
set <int> s;
vector<int>v;
//纵轴就不离散化了,把横轴离散化了
//map?
struct node{
int l;
int r;
int o;
bool is_plus;
};
map<int,vector<int> > tree;
map<int,vector<node > > problem;
int ans[MAXN];
//kaiyi的烦恼 o/QWQ\o
signed main(){
n=read(),m=read();
int x,y;
for(int i=0;i<n;i++){
x=read()+1;
y=read()+1;
tree[x].push_back(y);
s.insert(x);
}
v.push_back(0);
for(auto i:s)v.push_back(i);
int a=0,b=0,c=0,d=0;
for(int i=0;i<m;i++){
a=read()+1,b=read()+1,c=read()+1,d=read()+1;
int pos;
pos=lower_bound(v.begin(),v.end(),a)-v.begin()-1;
pos=v[pos];
problem[pos].push_back({b,d,i,false});
pos=upper_bound(v.begin(),v.end(),c)-v.begin()-1;
pos=v[pos];
problem[pos].push_back({b,d,i,true});
}
for(auto i:tree){
for(auto j:i.second){
modify(j,1);
}
for(auto j:problem[i.first]){
if(!j.is_plus){
ans[j.o]-=query(j.r)-query(j.l-1);
}
else{
ans[j.o]+=query(j.r)-query(j.l-1);
}
}
}
for(int i=0;i<m;i++)cout<<ans[i]<<endl;
return 0;
}