线段树区间染色poj2777&poj2528

本文深入探讨了线段树在区间染色及贴海报问题中的应用,详细介绍了两种操作的实现过程:一是将指定区间内所有元素染为同一颜色并查询颜色种类;二是贴海报过程中被覆盖的海报数量计算。通过具体代码示例,讲解了如何通过线段树高效处理区间更新和查询问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线段树区间染色,这个问题和一般的线段树有区别,更新和查询操作都需要注意。还要把数组开大一点。

参考(抄)了大佬的博客

poj2777

题意:涂颜色,有两种操作P和C,C :a,b,c三个数,表示把a,b区间内都涂成c这种颜色

P:a,b两个数,代表询问a,b区间内的颜色种类

#pragma warning(disable:4996)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<set>
#include<algorithm>
#include<climits>
using namespace std;
typedef long long ll;
bool vis[35];
int ans;
struct node
{
	int l, r, c;
};
node tree[300005 << 2];
//结构体数组开大一点
void build(int L, int R, int k)
{
	tree[k].l = L; tree[k].r = R;
	tree[k].c = 1;//最初的颜色都是1
	if (L == R)return;
	int mid = (L + R) / 2;
	build(L, mid, 2 * k);
	build(mid + 1, R, 2 * k + 1);
}
//建树
void update(int c, int L, int R, int k)
{
	if (tree[k].l >= L && R >= tree[k].r)//找到区间,改变颜色
	{
		tree[k].c = c;
		return;
	}
	if (tree[k].c)
	{
		tree[2 * k].c = tree[2 * k + 1].c = tree[k].c;
		tree[k].c = 0;
	}//下传操作
	int mid = (tree[k].l + tree[k].r) / 2;
	if (L <= mid) update(c, L, R, 2 * k);
	if (R > mid)update(c, L, R, 2 * k + 1);//更新左右儿子区间
	/*
	if (R <= mid)update(c, L, R, 2 * k);//完全在左边
	else if (L > mid)update(c, L, R, 2 * k + 1);//完全在右边
	else
	{
		update(c, L, mid, 2 * k);
		update(c, mid + 1, R, 2 * k + 1);
	}*/
}

void query(int k, int L, int R)
{
	if (tree[k].c)//区间没有其他颜色
	{
		if (!vis[tree[k].c])//没有被标记过
		{
			vis[tree[k].c] = 1;
			ans++;
		}
		return;
	}
	int mid = (tree[k].l + tree[k].r) / 2;
	if (R <= mid)query(2 * k, L, R);
	else if (L > mid)query(2 * k + 1, L, R);
	else
	{
		query(2 * k, L, mid);
		query(2 * k + 1, mid + 1, R);
	}
}
int main()
{
	int n, C, m, i, j, t1, t2, t3;
	char s[2];
	while (scanf("%d%d%d", &n, &C, &m) == 3)
	{
		memset(tree, 0, sizeof(tree));
		build(1, n, 1);
		while (m--)
		{
			scanf("%s", s);
			if (s[0] == 'C')
			{
				scanf("%d%d%d", &t1, &t2, &t3);
				if (t1 > t2)
				{
					j = t1;
					t1 = t2;
					t2 = j;
				}//处理t1>t2的情况
				update(t3, t1, t2, 1);
			}
			else
			{
				scanf("%d%d", &t1, &t2);
				ans = 0;
				memset(vis, 0, sizeof(vis));
				if (t1 > t2)
				{
					j = t1;
					t1 = t2;
					t2 = j;
				}
				query(1, t1, t2);
				printf("%d\n", ans);
			}
		}
	}
	return 0;
}

poj2528

题意:贴海报,海报的长度在1到1e7范围内,最多有1万张海报,先贴的海报会被后贴的海报覆盖

问最后有多少张海报可以被看见,露出一部分就可以

#pragma warning(disable:4996)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<set>
#include<algorithm>
#include<climits>
using namespace std;
typedef long long ll;
const int maxn = 20005;
int inl[maxn], inr[maxn],vis[maxn];
int a[maxn << 2],ans;
struct  node
{
	int l, r, c;
};
node tree[maxn << 4];
void build(int L, int R, int k)
{
	tree[k].l = L;tree[k].r = R;
	tree[k].c = 0;
	if (L == R)return;
	int mid = (L + R) / 2;
	build(L, mid, 2 * k);
	build(mid + 1, R, 2 * k + 1);
}
//建树
void update(int L, int R, int c, int k)
{
	if (L == tree[k].l && R == tree[k].r)
	{
		tree[k].c = c;
		return;
	}
	if (tree[k].c)
	{
		tree[2 * k].c = tree[k].c;
		tree[2 * k + 1].c = tree[k].c;
		tree[k].c = 0;
	}
	int mid = (tree[k].l + tree[k].r) / 2;
	if (R <= mid)update(L, R, c, 2 * k);
	else if (L > mid)update(L, R, c, 2 * k + 1);
	else
	{
		update(L, mid, c, 2 * k);
		update(mid + 1, R,c, 2 * k + 1);
	}
}
//更新
void query(int L, int R, int k)
{
	if (tree[k].c)
	{
		if (!vis[tree[k].c])
		{
			ans++;
			vis[tree[k].c] = 1;
		}
		return;
	}
	if (tree[k].l == tree[k].r)return;
	int mid = (tree[k].l + tree[k].r) / 2;
	if (R <= mid)query(L, R, 2 * k);
	else if (L > mid)query(L, R, 2 * k + 1);
	else
	{
		query(L, mid, 2 * k);
		query(mid + 1, R, 2 * k + 1);
	}
}
int main()
{
	int T, i, j, n,t;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d", &n);
		j = 0;
		for (i = 1;i <= n;i++)
		{
			scanf("%d%d", &inl[i], &inr[i]);
			a[j++] = inl[i];
			a[j++] = inr[i];
		}
		//把左右区间的数值输入,a数组下标从0到j-1
		sort(a, a + j);
		j=unique(a, a + j) - a;
		//先排序再去重
		t = j;
		for (i = 1;i < t;i++)
		{
			if (a[i] > (a[i - 1] + 1))
			{
				a[j++] = a[i - 1] + 1;
			}
		}
		//当相邻两项的差值大于1的时候,需要在中间加一个数
		//直接先加在数组的最后,然后再排序
		sort(a, a + j);
		memset(tree, 0, sizeof(tree));
		memset(vis, 0, sizeof(vis));
		//此时的j代表元素的个数,也是区间长度,线段树是从1开始的
		//所以,build(1,j,1)
		build(1, j, 1);
		//cout << " J " << j << endl;
		for (i = 1;i <= n;i++)
		{
			inl[i] = lower_bound(a, a + j, inl[i]) - a + 1;
			inr[i] = lower_bound(a, a + j, inr[i]) - a + 1;
			//cout << inl[i] << " " << inr[i] << endl;
			update(inl[i], inr[i], i, 1);
		}
		ans = 0;
		query(1, j, 1);
		printf("%d\n", ans);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值