洛谷P2184 贪婪大陆 线段树+染色问题


洛谷P2184 贪婪大陆


标签

  • 染色问题
  • 线段树

简明题意

  • 类似区间染色,操作1给出L,R涂色,操作2询问L,R的颜色数量。唯一不同的是这里的颜色会覆盖。n、m <= 1e5

思路

  • 首先是暴力涂色,然而你还需要记录每个节点具体的颜色,时间复杂度爆炸
  • 我们开两个数组num_l, num_r分别记录哪些地方有左端点,哪些地方有右端点。我们可以发现,当我们查询[L,R]区间地雷种类数时,我们用当前总的地雷种数 - 在L的左边的右端点数-在R右边的左端点数,即可得到当前答案。(如果不理解,请仔细思考一番)。这里的左端点数和右端点数可以通过前面的num_l,num_r直接for循环。这样,修改操作是O(1)的,查询操作需要for循环预处理,再查询,是O(n)的。总体来说,最坏的时间复杂度为O( n 2 n^2 n2),最好的情况下为O(n),这样能拿到60分。接下来我们考虑优化
  • 很显然,优化策略可以考虑线段树线段,区间查询,单点查询,写出来即可。

注意事项

  • 如果要写两颗线段树,可以写一个结构体,实例出两个对象,就可以简化代码了

总结

  • 类似容斥的题目。我们拘泥于一些容易维护的东西。这题要维护的东西显然和一般的题目不一样,他需要的是维护区间端点在任意一个区内的数量

AC代码

#include
#include
using namespace std;

const int maxn = 1e5 + 10;

struct Node
{
   int l, r, num;
};


int n, m;

struct Seg_tree
{
   Node tree[maxn * 4];

   void build(int o, int l, int r)
   {
   	tree[o].l = l, tree[o].r = r;
   	if (l == r)
   		return;

   	int mid = (l + r) / 2;
   	build(o * 2, l, mid);
   	build(o * 2 + 1, mid + 1, r);
   }

   void change(int o, int x)
   {
   	tree[o].num++;
   	if (tree[o].l == tree[o].r)
   		return;

   	int mid = (tree[o].l + tree[o].r) / 2;
   	if (x <= mid)
   		change(o * 2, x);
   	else
   		change(o * 2 + 1, x);
   }

   int ask(int o, int l, int r)
   {
   	if (l > n || r > n || l <= 0 || r <= 0) return 0;
   	if (l > r) return 0;
   	if (tree[o].l == l && tree[o].r == r)
   		return tree[o].num;

   	int mid = (tree[o].l + tree[o].r) / 2;
   	if (r <= mid)
   		return ask(o * 2, l, r);
   	else if (l > mid)
   		return ask(o * 2 + 1, l, r);
   	else
   		return ask(o * 2, l, mid) + ask(o * 2 + 1, mid + 1, r);
   }
};


Seg_tree t1, t2;

void solve()
{
   int cnt = 0;

   scanf("%d%d", &n, &m);
   t1.build(1, 1, n), t2.build(1, 1, n);

   for (int i = 1; i <= m; i++)
   {
   	int opt, l, r;
   	scanf("%d%d%d", &opt, &l, &r);

   	if (opt == 1)
   		cnt++, t1.change(1, l), t2.change(1, r);
   	else if (opt == 2)
   		printf("%d\n", cnt - t2.ask(1, 1, l - 1) - t1.ask(1, r + 1, n));
   }
}

int main()
{
   solve();
   return 0;
}

双倍经验

你可能感兴趣的:(#,线段树)