问题一、
这一题只涉及了查询线段树的最大值&最小值
Code
#include <iostream>
#include <utility>
#define lc (p<<1)
#define rc (p<<1|1)
using namespace std;
const int N = 5e4+10;
struct Node{
int l,r;
int maxn;
int minn;
}tree[N<<2];
int INF = (1<<29);
typedef pair<int,int> pii;
int a[N];
void build(int p,int l,int r){
tree[p].l = l;
tree[p].r = r;
tree[p].maxn = 0;
tree[p].minn = INF;
if(l==r){
tree[p].minn = tree[p].maxn = a[l];
return;
}
int mid = (l+r) >> 1;
build(lc,l,mid);
build(rc,mid+1,r);
tree[p].maxn = max(tree[lc].maxn,tree[rc].maxn);
tree[p].minn = min(tree[lc].minn,tree[rc].minn);
}
pii query(int p,int l,int r){
//完全在范围内
if(tree[p].l==l&&tree[p].r==r){
return make_pair(tree[p].maxn,tree[p].minn);
}
int mid = (tree[p].l+tree[p].r) >> 1;
//如果在左儿子的范围
if(r<=mid){
return query(lc,l,r);
//如果在右儿子的范围
}else if(l > mid){
return query(rc,l,r);
//如果是跨越的,分别求
}else{
pii p1 = query(lc,l,mid);
pii p2 = query(rc,mid+1,r);
return make_pair(max(p1.first,p2.first),min(p1.second,p2.second));
}
}
int main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int n,q;
cin >> n >> q;
for(int i=1;i<=n;i++) cin >> a[i];
build(1,1,n);
for(int i=1;i<=q;i++){
int l,r;
cin >> l >> r;
pii p = query(1,l,r);
cout << (p.first - p.second) << '\n';
}
return 0;
}
稍微解释一下几个query之间的值是怎么填的
1.如果完全坐落于当前节点所在的区间内;直接返回当前节点的就行;
//完全在范围内
if(tree[p].l==l&&tree[p].r==r){
return make_pair(tree[p].maxn,tree[p].minn);
}
如果完全位于左子树,那么这是一个子问题。我们递归解决即可;
//如果在左儿子的范围
if(r<=mid){
return query(lc,l,r);
}
如果完全位于右子树,那么同理有
//如果在右儿子的范围
}else if(l > mid){
return query(rc,l,r);
}
如果是横跨左右子树,那么我们得算好范围;
因为是横跨左右子树的
那么r必然大于mid
而l必然小于等于mid
(不理解的话看上面if条件就知道啦~)
显然区间[l,r]=[l,mid]+[mid+1,r]
那么对于左子树来说,需要查询的范围是[l,mid]
对于右子树来说,需要查询的范围是[mid+1,r]
分别查询后,再拼出结果即可。
那么就可以得到以下代码
else{
pii p1 = query(lc,l,mid);
pii p2 = query(rc,mid+1,r);
return make_pair(max(p1.first,p2.first),min(p1.second,p2.second));
}
问题二、
HDU4027
因为是算术平方根,我们没法直接区间操作;
因此这题是一个单点操作,区间查询的题目;
很容易想到,1的算术平方根还是它本身;
因此,如果某一个区间里面的数据都是1,我们就不要浪费时间去更新;
判断的方法很简单,看长度即可;
Code
#include <iostream>
#include <cmath>
#define lc (p<<1)
#define rc (p<<1|1)
using namespace std;
const int N = 100000+10;
typedef long long ll;
struct Node{
int l,r;
ll sum;
}tree[N<<2];
int INF = (1<<29);
ll a[N];
void push_up(int p){
tree[p].sum = tree[lc].sum + tree[rc].sum;
}
void build(int p,int l,int r){
tree[p].l=l,tree[p].r=r;
if(l==r){
tree[p].sum = a[l];
return;
}
int mid = (l+r) >> 1;
build(lc,l,mid);
build(rc,mid+1,r);
push_up(p);
}
void push_down(int p){
//如果是叶子
if(tree[p].l==tree[p].r){
tree[p].sum = (ll)sqrt(tree[p].sum);
return;
}
push_down(lc);
push_down(rc);
push_up(p);
}
void update(int p,int l,int r){
if(tree[p].l==l&&tree[p].r==r){
if(r-l+1==tree[p].sum) return;
push_down(p);
return;
}
int mid = (tree[p].l+tree[p].r)>>1;
if(r<=mid){
update(lc,l,r);
}else if(l>mid){
update(rc,l,r);
}else{
update(lc,l,mid);
update(rc,mid+1,r);
}
push_up(p);
}
ll query(int p,int l,int r){
if(tree[p].l==l&&tree[p].r==r){
return tree[p].sum;
}
int mid = (tree[p].l+tree[p].r)>>1;
if(r<=mid){
return query(lc,l,r);
}else if(l>mid){
return query(rc,l,r);
}else{
return query(lc,l,mid) + query(rc,mid+1,r);
}
}
int main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int n,m,t,x,y;
int num = 0;
while(cin>>n){
cout << "Case #"<< (++num) << ":\n";
for(int i=1;i<=n;i++) cin >> a[i];
build(1,1,n);
cin >> m;
for(int i=1;i<=m;i++){
cin >> t >> x >> y;
if(x>y) swap(x,y);
if(t==1){
cout << query(1,x,y) << '\n';
}else{
update(1,x,y);
}
}
cout << '\n';
}
return 0;
}
问题三、
POJ2481
其实就是在问:对于每一个闭区间,求它是多少个区间的真子集。
首先我们按区间左端点 S为第一关键字,升序排序;如果 S 相等则对右端点 E 进行降序排序;
排序后我们进行遍历,现在已经保证了越靠前的范围越大;
如果i<j 那么必然有Si <= Sj
当Si 等于 Sj 的时候,如果 Ti 也等于 Tj,那么直接继承;
否则
我们只需要去查找有多少个右端点
比当前枚举到的这个点的右端点大;
就可以得到真子集了;
#include <iostream>
#include <algorithm>
#include <cstdio>
#define lc (p<<1)
#define rc (p<<1|1)
using namespace std;
const int N = 1e5+10;
struct Cow{
Cow(int l,int r,int p) :l(l),r(r),pos(p){}
Cow(){}
int l,r,pos;
}c[N];
struct Tree{
int l,r,sum;//sum存的是目前区间内有的右端点个数
//对于指定的右端点,只需要query有多少个右端点是≥它的
}tree[N<<2];
bool cmp(Cow a,Cow b){
if(a.l==b.l) return a.r > b.r;
return a.l < b.l;
}
void build(int p,int l,int r){
tree[p].l=l,tree[p].r=r,tree[p].sum=0;
if(l==r){
return;
}
int mid = (l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
}
int ans[N];
int query(int p,int l,int r){
if(tree[p].l>=l&&tree[p].r<=r){
return tree[p].sum;
}
int mid = (tree[p].l+tree[p].r) >> 1;
if(mid>=r){
return query(lc,l,r);
}else if(l>mid){
return query(rc,l,r);
}else{
return query(lc,l,mid)+query(rc,mid+1,r);
}
}
void update(int p,int target){
if(tree[p].l==tree[p].r){
tree[p].sum++;
return;
}
int mid = (tree[p].l+tree[p].r) >> 1;
if(target<=mid){
update(lc,target);
}else{
update(rc,target);
}
tree[p].sum = tree[lc].sum + tree[rc].sum;
}
int main()
{
//std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int n,l,r;
while(~scanf("%d",&n)&&n){
int maxn = 0;
for(int i=1;i<=n;i++){
//cin >> l >> r;
scanf("%d%d",&l,&r);
maxn = max(maxn,r);
c[i] = Cow(l,r,i);
}
sort(c+1,c+1+n,cmp);
build(1,1,maxn);
for(int i=1;i<=n;i++){
if(i!=1&&c[i].l==c[i-1].l&&c[i].r==c[i-1].r){
ans[c[i].pos] = ans[c[i-1].pos];
}else{
ans[c[i].pos] = query(1,c[i].r,maxn);
}
//当前右端点所属的区间都需要更新
update(1,c[i].r);
}
for(int i=1;i<=n;i++){
printf("%d ",ans[i]);
//cout << ans[i] << ' ';
}
putchar('\n');
//cout << '\n';
}
return 0;
}