[Codeforces Round #444 div1] C.DZY Loves Colors 【线段树】

题目链接:CF Round #444 div1 C

题目分析

这道题目是要实现区间赋值的操作,同时还要根据区间中原先的值修改区间上的属性权值。

如果直接使用普通的线段树区间赋值的方法,当一个节点表示的区间完全被要求修改的区间包含时,就直接打上赋值的标记然后 return 。但是这样这个节点中每个位置原先的值不同,需要进行的属性权值修改也就不同,是不能直接实现的。如果我们在节点表示的区间被修改的区间包含时,并不直接打标记 return ,而是当节点表示的区间被修改的区间完全包含而且这个节点中的每个位置的颜色相同时,才直接打标记然后 return ,否则就继续递归下去。这样就可以直接打标记修改属性权值了。但是这样看起来是会使复杂度退化的,但是实际上通过一些我不懂的势能分析,这样修改复杂度还是 O(log n) 的。所以这样这道题就变成线段树水题了。

需要维护一下每个节点表示的区间是否颜色相同。

区间赋值的题目就可以使用这种修改方式。

代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

typedef long long LL;

inline int Abs(int x) {return x < 0 ? -x : x;}

const int MaxN = 100000 + 5;

int n, m;
int Col[MaxN * 4], Len[MaxN * 4], Pc[MaxN * 4];

LL T[MaxN * 4], D[MaxN * 4];

bool Same[MaxN * 4];

inline void Update(int x)
{
	Same[x] = Same[x << 1] && Same[x << 1 | 1] && Col[x << 1] == Col[x << 1 | 1];
	if (Same[x]) Col[x] = Col[x << 1];
	T[x] = T[x << 1] + T[x << 1 | 1];
	Len[x] = Len[x << 1] + Len[x << 1 | 1];
}

inline void Add(int x, LL Num)
{
	D[x] += Num;
	T[x] += (LL)Len[x] * Num;
}

inline void PushDown(int x)
{
	if (D[x] != 0)
	{
		Add(x << 1, D[x]);
		Add(x << 1 | 1, D[x]);
		D[x] = 0;
	}
	if (Pc[x] != 0)
	{
		Col[x << 1] = Pc[x << 1] = Pc[x];
		Col[x << 1 | 1] = Pc[x << 1 | 1] = Pc[x];
		Pc[x] = 0;
	}
}

void Build(int x, int s, int t)
{
	if (s == t)
	{
		Same[x] = true;
		Col[x] = s;
		T[x] = D[x] = Pc[x] = 0;
		Len[x] = 1;
		return;
	}
	int m = (s + t) >> 1;
	Build(x << 1, s, m);
	Build(x << 1 | 1, m + 1, t);
	Update(x);
}

void Change(int x, int s, int t, int l, int r, int Num)
{
	if (l <= s && r >= t && Same[x])
	{
		int Temp = Abs(Col[x] - Num);
		D[x] += (LL)Temp;
		T[x] += (LL)Temp * (LL)Len[x];
		Col[x] = Pc[x] = Num;
		return;
	}
	PushDown(x);
	int m = (s + t) >> 1;
	if (l <= m) Change(x << 1, s, m, l, r, Num);
	if (r >= m + 1) Change(x << 1 | 1, m + 1, t, l, r, Num);
	Update(x);
}

LL Query(int x, int s, int t, int l, int r)
{
	if (l <= s && r >= t) return T[x];
	PushDown(x);
	int m = (s + t) >> 1;
	LL ret = 0;
	if (l <= m) ret += Query(x << 1, s, m, l, r);
	if (r >= m + 1) ret += Query(x << 1 | 1, m + 1, t, l, r);
	return ret;
}

int main()
{
	scanf("%d%d", &n, &m);
	Build(1, 1, n);
	int f, l, r, Num;
	for (int i = 1; i <= m; ++i)
	{
		scanf("%d%d%d", &f, &l, &r);
		if (f == 1)
		{
			scanf("%d", &Num);
			Change(1, 1, n, l, r, Num);
		}
		else printf("%I64d\n", Query(1, 1, n, l, r));
	}
	return 0;
}

  

时间: 2024-08-15 11:59:21

[Codeforces Round #444 div1] C.DZY Loves Colors 【线段树】的相关文章

codeforces 444 C. DZY Loves Colors(线段树)

题目大意: 1 l r x操作 讲 [l,r]上的节点涂成x颜色,并且每个节点的值都加上 |y-x| y为涂之前的颜色 2 l r  操作,求出[l,r]上的和. 思路分析: 如果一个区间为相同的颜色.那么我们才可以合并操作. 所以我们之前找相同的区间就好. 但是问题是如何合并操作. 那么我们定义一个val  表示这个区间每个位置上应该加上的值. pushdown 的时候这个值是可以相加的. #include <cstdio> #include <iostream> #includ

Codeforces 444C DZY Loves Colors(线段树)

题目大意:Codeforces 444C DZY Loves Colors 题目大意:两种操作,1是修改区间上l到r上面德值为x,2是询问l到r区间总的修改值. 解题思路:线段树模板题. #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using namespace std; const int maxn = 5*1e5; typedef long lo

codeforces - 444c DZY Loves Colors(线段树+染色)

C. DZY Loves Colors time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output DZY loves colors, and he enjoys painting. On a colorful day, DZY gets a colorful ribbon, which consists of n units (they

Codeforces Round #FF(255) C. DZY Loves Sequences (LIS升级)

题目:C. DZY Loves Sequences (LIS升级) 题意: 在n个数中,最多改变一个数字,并求能够达到的最长严格上升子序列(连续)长度 分析: 考虑第i个数,能否改变后拼接前后两个字串,并维护当前最大值 状态: left[i]:  表示以i为终点的最长严格上升子序列长度 right[i]: 表示以i为起点的最长严格上升子序列长度 dp[i]:   表示改变第i个数后,拼接前后字串的长度 转移方程:       dp[i] = max{left[i-1] + right[i+1] 

Codeforces Round #254 (Div. 2) DZY Loves Chemistry【并查集基础】

一开始不知道题意是啥意思,迟放进去反应和后放进去反应有什么区别 对于第三组数据不是很懂,为啥312,132的组合是不行的 后来发现这是一道考察并查集的题目 QAQ 怒贴代码: 1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <math.h> 5 #include <iostream> 6 #include <algorithm> 7

Codeforces Round #FF/#255 D DZY Loves Modification --贪心+优先队列

题意:给你一个矩阵,每次选某一行或者某一列,得到的价值为那一行或列的和,然后该行每个元素减去p.问连续取k次能得到的最大总价值为多少. 解法: 如果p=0,即永远不减数,那么最优肯定是取每行或每列那个最大的取k次,所以最优解由此推出. 如果不管p,先拿,最后再减去那些行列交叉点,因为每个点的值只能取一次,而交叉点的值被加了两次,所以要减掉1次,如果取行A次,取列B次,那么最后答案为: res = dp1[A] + dp2[B] - B*(k-A)*p,可以细细体会一下后面那部分. 其中: dp1

Codeforces Round #603 (Div. 2) E. Editor(线段树)

链接: https://codeforces.com/contest/1263/problem/E 题意: The development of a text editor is a hard problem. You need to implement an extra module for brackets coloring in text. Your editor consists of a line with infinite length and cursor, which point

Codeforces Round #426 (Div. 2) D. The Bakery(线段树维护dp)

题目链接: Codeforces Round #426 (Div. 2) D. The Bakery 题意: 给你n个数,划分为k段,每段的价值为这一段不同的数的个数,问如何划分,使得价值最大. 题解: 考虑dp[i][j]表示划分为前j个数划分为i段的最大价值,那么这就是一个n*n*k的dp, 考虑转移方程dp[i][j]=max{dp[i][k]+val[k+1][j]},我们用线段树去维护这个max,线段树上每个节点维护的值是dp[i][k]+val[k+1][j],对于每加进来的一个数a

Codeforces Round #424 (Div. 2) E. Cards Sorting(线段树)

题目链接:Codeforces Round #424 (Div. 2) E. Cards Sorting 题意: 将n个数放进一个队列,每次检查队首,看看是不是队列中最小的数,如果是就扔掉,如果不是就放到队尾. 这样直到队列为空,为需要操作多少次. 题解: 考虑用两个指针模拟,最开始now指针指向第一个数,然后nxt指针指向下一个将要被删除的数. 然后我们要算出这里需要移动多少步,然后删掉这个数,一直重复操作,直到将全部的数删完. nxt指针可以用set来维护,now指针可以用并查集来维护. 计