【题解】贪婪大陆

线段树差分经典题。

题目链接

解:

对于题目中要求的维护区间颜色种数,暴力维护必定不可取。于是,我们考虑线段树的性质:从下向上维护左右区间信息。

那么,我们可不可以利用这一点来简单维护它的颜色种类数呢?

我们可以维护一组地雷(颜色)的开头和结尾。

显然,我们这样可以任意求出任意区间的开头数和结尾数。那么,复杂的区间修改就变成了单点修改。

注意线段树的性质,也就是说,即使它的开头不在询问区间,只要它覆盖了,就一定不会遗漏。

那么,套用差分公式,r-l+1,即

query_s(1,r,1)-query_e(1,l-1,1)

其中,query_s是查询开头数,query_e是查询结尾数。

问题得解。

代码:

#include<cstdio>
#include<iostream>
using namespace std;
int n,m,opt,x,y;
struct node{
    int l,r,st,ed;
}tr[500000];
void build(int l,int r,int x){
    tr[x].l=l;tr[x].r=r;
    if(l==r){
        tr[x].st=tr[x].ed=0;
        return;
    }int mid=l+r>>1;
    build(l,mid,x<<1);
    build(mid+1,r,x<<1|1);
    tr[x].ed=tr[x<<1].ed+tr[x<<1|1].ed;
    tr[x].st=tr[x<<1].st+tr[x<<1|1].st;
}
void change_s(int goal,int x){
    if(tr[x].l==tr[x].r)tr[x].st++;
    else{
        int mid=(tr[x].l+tr[x].r)>>1;
        if(goal<=mid)change_s(goal,x<<1);
        else change_s(goal,x<<1|1);
        tr[x].st=tr[x<<1].st+tr[x<<1|1].st;
    }
}
void change_e(int goal,int x){
    if(tr[x].l==tr[x].r)tr[x].ed++;
    else{
        int mid=(tr[x].l+tr[x].r)>>1;
        if(goal<=mid)change_e(goal,x<<1);
        else change_e(goal,x<<1|1);
        tr[x].ed=tr[x<<1].ed+tr[x<<1|1].ed;
    }
}
int query_s(int l,int r,int x){
    if(tr[x].l==l&&tr[x].r==r)return tr[x].st;
    else{
        int mid=(tr[x].l+tr[x].r)>>1;
        if(r<=mid)return query_s(l,r,x<<1);
        else if(mid<l)return query_s(l,r,x<<1|1);
        else return query_s(l,mid,x<<1)+query_s(mid+1,r,x<<1|1);
    }
}
int query_e(int l,int r,int x){
    if(tr[x].l==l&&tr[x].r==r)return tr[x].ed;
    else{
        int mid=tr[x].l+tr[x].r>>1;
        if(r<=mid)return query_e(l,r,x<<1);
        else if(mid<l)return query_e(l,r,x<<1|1);
        else return query_e(l,mid,x<<1)+query_e(mid+1,r,x<<1|1);
    }
}
int main(){
    scanf("%d%d",&n,&m);
    build(1,n,1);
    for(int i=1;i<=m;++i){
        scanf("%d%d%d",&opt,&x,&y);
        if(opt==1)change_s(x,1),change_e(y,1);
        else printf("%d\n",query_s(1,y,1)-query_e(1,x-1,1));
    }
    return 0;
} 

原文地址:https://www.cnblogs.com/h-lka/p/11191005.html

时间: 2024-08-30 10:25:21

【题解】贪婪大陆的相关文章

luoguP2184 贪婪大陆 题解(树状数组)

P2184 贪婪大陆  题目 其实很容易理解就是询问一段区间内有多少段不同的区间 然后再仔细思索一下会发现: 1.只要一个区间的开头在一个节点i的左边,那么这个区间包含在区间1~i中. 2.只要一个区间的尾部在一个节点j的左边,那么这个区间肯定不属于j之后的所有区间 这时候就不难想到用两个树状数组维护: 第一个:维护节点i之前有多少个区间的开头 第二个:维护节点j之前有多少个区间的结尾 不难证明拿sum[i]-sum[j]得到的就是i~j中间地雷的个数(手动模拟一波就一清二楚了) #includ

P2184 贪婪大陆

P2184 贪婪大陆 题目背景 面对蚂蚁们的疯狂进攻,小FF的Tower defence宣告失败……人类被蚂蚁们逼到了Greed Island上的一个海湾.现在,小FF的后方是一望无际的大海, 前方是变异了的超级蚂蚁. 小FF还有大好前程,他可不想命丧于此, 于是他派遣手下最后一批改造SCV布置地雷以阻挡蚂蚁们的进攻. 题目描述 小FF最后一道防线是一条长度为N的战壕, 小FF拥有无数多种地雷,而SCV每次可以在[ L , R ]区间埋放同一种不同于之前已经埋放的地雷. 由于情况已经十万火急,小

AC日记——贪婪大陆 洛谷 P2184

贪婪大陆 思路: 树状数组: 跪烂.. 代码: #include <bits/stdc++.h> using namespace std; #define maxn 100005 int n,m,ltree[maxn],rtree[maxn],tot; inline void in(int &now) { char Cget=getchar();now=0; while(Cget>'9'||Cget<'0')Cget=getchar(); while(Cget>='0

cogs——1008. 贪婪大陆(清华巨佬代码)——树状数组

1008. 贪婪大陆 ★★   输入文件:greedisland.in   输出文件:greedisland.out   简单对比时间限制:1 s   内存限制:128 MB 试题四:贪婪大陆  [题目描述]  面对蚂蚁们的疯狂进攻,小FF的Tower defense宣告失败……人类被蚂蚁们逼到了Greed Island上的一个海湾.现在,小FF的后方是一望无际的大海,前方是变异了的超级蚂蚁. 小FF还有大好前程,他可不想命丧于此, 于是他派遣手下最后一批改造SCV布置地雷以阻挡蚂蚁们的进攻. 

COGS1008. 贪婪大陆[树状数组 模型转换]

1008. 贪婪大陆 ★★   输入文件:greedisland.in   输出文件:greedisland.out   简单对比时间限制:1 s   内存限制:128 MB 试题四:贪婪大陆  [题目描述]  面对蚂蚁们的疯狂进攻,小FF的Tower defense宣告失败……人类被蚂蚁们逼到了Greed Island上的一个海湾.现在,小FF的后方是一望无际的大海,前方是变异了的超级蚂蚁. 小FF还有大好前程,他可不想命丧于此, 于是他派遣手下最后一批改造SCV布置地雷以阻挡蚂蚁们的进攻. 

luogu题解 P2184 【贪婪大陆】

题目链接: https://www.luogu.org/problemnew/show/P2184 思路: 首先我想吐槽一下为什么现有题解中的做法都是一样的,而且还比较难以理解; 我就讲下我的做法,本质上是一样的,但是跟容易理解. 根据题意每加一次地雷就多一个种类对吧,我们用一个cnt记录加过地雷的次数,同时分别用两个数组记录左右两个端点的位置.然后查询[l,r]时呢,我们分别查询[1,l-1]有多少个右端点,[r+1,n]有多少个左端点,然后这两个数的和是什么意思呢?就是有多少次铺地雷没铺到我

贪婪大陆

题目背景 面对蚂蚁们的疯狂进攻,小FF的Tower defence宣告失败--人类被蚂蚁们逼到了Greed Island上的一个海湾.现在,小FF的后方是一望无际的大海, 前方是变异了的超级蚂蚁. 小FF还有大好前程,他可不想命丧于此, 于是他派遣手下最后一批改造SCV布置地雷以阻挡蚂蚁们的进攻. 题目描述 小FF最后一道防线是一条长度为N的战壕, 小FF拥有无数多种地雷,而SCV每次可以在[ L , R ]区间埋放同一种不同于之前已经埋放的地雷. 由于情况已经十万火急,小FF在某些时候可能会询

[luoguP2184] 贪婪大陆(树状数组)

传送门 用两个树状数组,cr 维护 1....x 中 r 的数量 cl 维护 1....x 中 l 的数量 求答案的时候只需要求 y 前面 被作为左端点 的个数 - x 前面 被作为右端点的个数 ——代码 1 #include <cstdio> 2 3 using namespace std; 4 5 const int MAXN = 1000001; 6 int n, m; 7 int cl[MAXN], cr[MAXN]; 8 9 inline void add1(int x) { for

cogs 1008 贪婪大陆

/* 不要思维定视 盯着线段树维护l r 的ans不放 显然没法区间合并 换一种思路 如果打暴力的话 O(nm) 每次询问 扫一遍之前所有的修改 有交点则说明种数++ 接下来考虑如何优化 我们把每个区间看做(l,r)的坐标内的点 然后查询区间L<=li<=R L<=ri<=R内有多少点 这样的查询在二维坐标里不会查(反正我不会..)将坐标轴转化成两个一维的线段 这样我们就又面临一个问题 会有重复 那就用排除法 总数-不重复区间的 又因为保证了 r>=l 所以l轴的[r+1,n