线段树博客

知乎上的线段树
线段树从0开始
线段树学习
在这里插入图片描述

递归建树的动态图解
递归建树中的 rt 表示的是当前节点的实际存储位置

洛谷 线段树模板题一
区间修改,维护区间和

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
ll tree[MAXN << 2], mark[MAXN << 2], n, m, A[MAXN];
void push_down(int p, int len)
{
    tree[p << 1] += mark[p] * (len - len / 2);
    mark[p << 1] += mark[p];
    tree[p << 1 | 1] += mark[p] * (len / 2);
    mark[p << 1 | 1] += mark[p];
    mark[p] = 0;
}
void build(int p = 1, int cl = 1, int cr = n)
// 函数定义参数的时候赋值,如果没传参数,就是赋予的值,否则就是传递的值
{
    if (cl == cr) { tree[p] = A[cl]; return; }
    int mid = (cl + cr) >> 1;
    build(p << 1, cl, mid);
    build(p << 1 | 1, mid + 1, cr);
    tree[p] = tree[p << 1] + tree[p << 1 | 1];
}
ll query(int l, int r, int p = 1, int cl = 1, int cr = n)
{
    if (cl >= l && cr <= r) return tree[p];
    push_down(p, cr - cl + 1);
    ll mid = (cl + cr) >> 1, ans = 0;
    if (mid >= l) ans += query(l, r, p << 1, cl, mid);
    if (mid < r) ans += query(l, r, p << 1 | 1, mid + 1, cr);
    return ans;
}
void update(int l, int r, int d, int p = 1, int cl = 1, int cr = n)
{
    if (cl >= l && cr <= r) { tree[p] += d * (cr - cl + 1), mark[p] += d; return; }
    // 只有整个区间被修改的节点才会有懒惰标记
    push_down(p, cr - cl + 1);
    int mid = (cl + cr) >> 1;
    if (mid >= l) update(l, r, d, p << 1, cl, mid);
    if (mid < r) update(l, r, d, p << 1 | 1, mid + 1, cr);
    tree[p] = tree[p << 1] + tree[p << 1 | 1];
}
int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> A[i];
    build();
    while (m--)
    {
        int o, l, r, d;
        cin >> o >> l >> r;
        if (o == 1)
            cin >> d, update(l, r, d);
        else
            cout << query(l, r) << '\n';
    }
    return 0;
}

update:
新码风

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
ll tr[maxn], mark[maxn];
#define ls p << 1
#define rs p << 1 | 1

void pushup(int p){
	tr[p] = (tr[ls] + tr[rs]);
}
void md(int p, int len, ll d){// 要更新的节点编号,节点代表的期区间长度,以及要更新的值d 
	tr[p] += d * len;
	mark[p] += d;
}
void pushdown(int p, int len){
	if(!mark[p]) return;
	md(ls, len - len / 2, mark[p]);
	md(rs, len / 2, mark[p]);
	mark[p] = 0;
}
void build(int p = 1, int cl = 1, int cr = n){
	if(cl == cr){
		cin >> tr[p];
		return;
	}
	int mid = cl + cr >> 1;
	build(ls, cl, mid);
	build(rs, mid + 1, cr);
	pushup(p);
}
void update(int l, int r, ll d, int p = 1, int cl = 1, int cr = n){
	if(cl >= l && cr <= r){
		md(p, cr - cl + 1, d);return;
	}
	pushdown(p, cr - cl + 1);
	int mid = cl + cr >> 1;
	if(mid >= l) update(l, r, d, ls, cl, mid);
	if(mid < r) update(l, r, d, rs, mid + 1, cr);
	pushup(p);
}
ll qry(int l, int r, int p = 1, int cl = 1, int cr = n){
	if(cl >= l && cr <= r) return tr[p];
	pushdown(p, cr - cl + 1);
	int mid = cl + cr >> 1;
	ll ans = 0;
	if(mid >= l) ans += qry(l, r, ls, cl, mid);
	if(mid < r) ans += qry(l, r, rs, mid + 1, cr);
	return ans;
}
void work()
{
	cin >> n >> m;
	build();
	while(m--){
		ll op, l, r;cin >> op >> l >> r;
		if(op == 1){
			ll d;cin >> d;update(l, r, d);
		}
		else cout << qry(l, r) << endl;
	}
}

int main()
{
	ios::sync_with_stdio(0);
//	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

洛谷 线段树模板题二
先乘后加的原因。。。

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
int mod = 571373;
typedef long long ll;
const int N = 1e5 + 9;
ll tree[N << 2], add_mark[N << 2], mul_mark[N << 2], a[N], n, m;
void build(int p = 1, int cl = 1, int cr = n)
{
	if(cl == cr){
		tree[p] = a[cl] % mod; return;
	}
	int mid = (cl + cr) >> 1;
	build(p << 1, cl, mid);
	build(p << 1 | 1, mid + 1, cr);
	tree[p] = (tree[p << 1] + tree[p << 1 | 1]) % mod;
}
void pushdown(int p, int len)
{
	tree[p << 1] = (mul_mark[p] * tree[p << 1] + ((len - len / 2) * add_mark[p]) % mod ) % mod;
	mul_mark[p << 1] = (mul_mark[p << 1] * mul_mark[p]) % mod;
	add_mark[p << 1] = (add_mark[p << 1] * mul_mark[p] + add_mark[p]) % mod;
	
	tree[p << 1 | 1] = (mul_mark[p] * tree[p << 1 | 1] + ((len / 2) * add_mark[p]) % mod ) % mod;
	mul_mark[p << 1 | 1] = (mul_mark[p << 1 | 1] * mul_mark[p]) % mod;
	add_mark[p << 1 | 1] = (add_mark[p << 1 | 1] * mul_mark[p] + add_mark[p]) % mod;
	
	mul_mark[p] = 1, add_mark[p] = 0;
}
void add(int l, int r, int d,int p = 1, int cl = 1, int cr = n)
{
	if(cl >= l && cr <= r){
		add_mark[p] += d; add_mark[p] %= mod;
		tree[p] += d * (cr - cl + 1); tree[p] %= mod;  return;
	}
	pushdown(p, cr - cl + 1);
	int mid = (cr + cl) >> 1;
	if(l <= mid) add(l, r, d, p << 1, cl, mid);
	if(r > mid) add(l, r, d, p << 1 | 1, mid + 1, cr);
	tree[p] = (tree[p << 1] + tree[p << 1 | 1]) % mod;
}
void mul(int l, int r, int d, int p = 1,int cl = 1, int cr = n)
{
	if(cl >= l && cr <= r){
		add_mark[p] = (add_mark[p] * d) % mod;
		mul_mark[p] = (mul_mark[p] * d) % mod;
		tree[p] = (tree[p] * d) % mod;return;
	}
	pushdown(p, cr - cl + 1);
	int mid = (cl + cr) >> 1;
	if(l <= mid) mul(l, r, d, p << 1, cl, mid);
	if(r > mid) mul(l, r, d, p << 1 | 1, mid + 1, cr);
	tree[p] = (tree[p << 1] + tree[p << 1 | 1]) % mod;
}
ll query(int l, int r, int p = 1, int cl = 1, int cr = n)
{
	if(cl >= l && cr <= r) return tree[p] % mod;
	pushdown(p, cr - cl + 1);
	ll mid = (cl + cr) >> 1, ans = 0;
	if(l <= mid) ans += query(l, r, p << 1, cl, mid), ans %= mod;
	if(mid < r) ans += query(l, r, p << 1 | 1, mid + 1, cr), ans %= mod;
	return ans % mod;
}
int main()
{
	for(int i = 1; i <= N*4 - 9; ++i) mul_mark[i] = 1;
	cin >> n >> m >> mod;
	for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
	build();
	while(m--)
	{
		int o, l, r, w;
		scanf("%d", &o);
		if(o == 1){
			scanf("%d %d %d", &l, &r, &w);
			mul(l, r, w);
		}
		else if(o == 2){
			scanf("%d %d %d", &l, &r, &w);
			add(l, r, w);
		}
		else {
			scanf("%d %d", &l, &r);
			cout << query(l, r) << endl;
		}
	}
}

update:
题解
在对线段树有一定理解的基础上,看这个博客仔细想一下就能差不多明白为什么先乘后加
我说一下我的理解:
在实行 m u l mul mul 操作的时候, a d d _ m a r k [ p ] add\_mark[p] add_mark[p] 也对应的乘了 d d d,相当于把之前的 a d d add add 值乘出来
就是 ( o r i g n a l + 3 ) ∗ 4 = o r i g n a l ∗ 4 + 3 ∗ 4 (orignal+3)*4=orignal*4+3*4 (orignal+3)4=orignal4+34 a d d add add + 3 +3 +3 变成了 + 12 +12 +12,因此在 p u s h d o w n pushdown pushdown 更新子树值的时候,应该先 m u l mul mul 更新 o r i g n a l orignal orignal,然后再加上 a d d add add 就好了
新码风

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
int mod = 571373;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
ll tr[maxn], add_mark[maxn], mul_mark[maxn];
#define ls p << 1
#define rs p << 1 | 1

void pushup(int p){
	tr[p] = (tr[ls] + tr[rs]) % mod;
}

void add_md(int p, int len, ll d){
	(tr[p] += len * d) %= mod;
	(add_mark[p] += d) %= mod;
}
void mul_md(int p, ll d){
	(tr[p] *= d) %= mod;
	(add_mark[p] *= d) %= mod;
	(mul_mark[p] *= d) %= mod;
}
void pushdown(int p, int len){
	mul_md(ls, mul_mark[p]);
	add_md(ls, len - len / 2, add_mark[p]);
	mul_md(rs, mul_mark[p]);
	add_md(rs, len / 2, add_mark[p]);
	add_mark[p] = 0, mul_mark[p] = 1;
}
void build(int p = 1, int cl = 1, int cr = n){
	if(cl == cr){
		cin >> tr[p];
		return;
	}
	int mid = cl + cr >> 1;
	build(ls, cl, mid);
	build(rs, mid + 1, cr);
	pushup(p);
}
void add(int l, int r, ll d, int p = 1, int cl = 1, int cr = n){
	if(cl >= l && cr <= r){
		add_md(p, cr - cl + 1, d);return;
	}
	pushdown(p, cr - cl + 1);
	int mid = cl + cr >> 1;
	if(mid >= l) add(l, r, d, ls, cl, mid);
	if(mid < r) add(l, r, d, rs, mid + 1, cr);
	pushup(p);
}
void mul(int l, int r, ll d, int p = 1, int cl = 1, int cr = n){
	if(cl >= l && cr <= r){
		mul_md(p, d);return;
	}
	pushdown(p, cr - cl + 1);
	int mid = cl + cr >> 1;
	if(mid >= l) mul(l, r, d, ls, cl, mid);
	if(mid < r) mul(l, r, d, rs, mid + 1, cr);
	pushup(p);
}
ll qry(int l, int r, int p = 1, int cl = 1, int cr = n){
	if(cl >= l && cr <= r) return tr[p];
	pushdown(p, cr - cl + 1);
	int mid = cl + cr >> 1;
	ll ans = 0;
	if(mid >= l) (ans += qry(l, r, ls, cl, mid)) %= mod;
	if(mid < r) (ans += qry(l, r, rs, mid + 1, cr)) %= mod;
	return ans;
}
void work()
{
	for(int i = 1; i <= maxn - 9; ++i) mul_mark[i] = 1;// 记得初始化
	cin >> n >> m >> mod;
	build();
	while(m--){
		ll op, l, r, d;cin >> op >> l >> r;
		if(op == 1){
			cin >> d;mul(l, r, d);
		}
		else if(op == 2){
			cin >> d;add(l, r, d);
		}
		else cout << qry(l, r) << endl;
	}
}

int main()
{
	ios::sync_with_stdio(0);
//	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

赛氪 线段树

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 5;
int tree[MAXN << 2];
// 只要这个开 int 就能 a    垃圾oj
ll mark[MAXN << 2], n, m;
void push_down(int p, int len)
{
    tree[p << 1] += mark[p] * (len - len / 2);
    mark[p << 1] += mark[p];
    tree[p << 1 | 1] += mark[p] * (len / 2);
    mark[p << 1 | 1] += mark[p];
    mark[p] = 0;
}
ll query(int l, int r, int p = 1, int cl = 1, int cr = n)
{
    if (cl >= l && cr <= r) return tree[p];
    push_down(p, cr - cl + 1);
    ll mid = (cl + cr) >> 1, ans = 0;
    if (mid >= l) ans += query(l, r, p << 1, cl, mid);
    if (mid < r) ans += query(l, r, p << 1 | 1, mid + 1, cr);
    return ans;
}
void update(int l, int r, int d, int p = 1, int cl = 1, int cr = n)
{
    if (cl >= l && cr <= r) { tree[p] += d * (cr - cl + 1), mark[p] += d; return; }
    // 只有整个区间被修改的节点才会有懒惰标记
    push_down(p, cr - cl + 1);
    int mid = (cl + cr) >> 1;
    if (mid >= l) update(l, r, d, p << 1, cl, mid);
    if (mid < r) update(l, r, d, p << 1 | 1, mid + 1, cr);
    tree[p] = tree[p << 1] + tree[p << 1 | 1];
}
int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;
    while (m--)
    {
        int o, l, r, d;
        cin >> o;
        if (o == 0)
        {
        	cin >> l >> r >> d;
        	if(l != 1) update(1, l-1, d * (r - l + 1));
			if(r != n) update(r + 1, n, d * (r - l + 1));
		}
        else
        {
        	cin >> l;
        	cout << query(l ,l) << endl;
		}
    }
    return 0;
}

另外解法
虽然这个代码ac了,但是 不用开 ll 就能ac就离谱,并且开 ll 就wa
其实用线段树的思路跟这个差不多

#include<iostream>
using namespace std;
const int N = 200010;
typedef long long ll;
int n, m;
int ans[200010], sum, op, l, r, z;
int main()
{
	ios::sync_with_stdio(false);
	cin >> n >> m;
	while(m --)
	{
		cin >> op;
		if(op == 1)
		{
			cin >> l;
			cout << sum - ans[l] << endl;
		}
		else
		{
			cin >> l >> r >> z;
			for(int i = l;i <= r; ++i)
				ans[i] += (r - l + 1) * z;
			sum += (r - l + 1) * z;
		}
	}
	return 0;
}

VJ刷题

kuangbin大佬的题解

hdu-1754-修改点为某个值,查询维护区间内最大值
题意:查询区间最大值,每次修改将第 i d id id 个学生的成绩由 a a a 改成 b b b
单点修改,维护区间最大值
查询 或 更新时,维护区间内的最大值即可,好像也没必要 pushdown(因为是维护区间最值并且是单点修改)
code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 9;
ll tree[N << 2], mark[N << 2], n, m, A[N], t, q;
void pushdown(int p, int len)// 可以注释掉
{
	tree[p << 1] += mark[p] * (len - len / 2);
	mark[p << 1] += mark[p];
	tree[p << 1 | 1] += mark[p] * (len / 2);
	mark[p << 1 | 1] += mark[p];
	mark[p] = 0;
}
void build(int p = 1, int cl = 1, int cr = n)
{
	if(cl == cr) {
		tree[p] = A[cl]; return;
	}
	int mid = (cl + cr) >> 1;
	build(p << 1, cl, mid);
	build(p << 1 | 1, mid + 1, cr);
	tree[p] = max(tree[p << 1], tree[p << 1 | 1]);
}
ll query(int l,int r, int p = 1, int cl = 1, int cr = n)
{
	if(cl >= l && cr <= r) return tree[p];
	//pushdown(p, cr - cl + 1);
	ll mid = (cl + cr) >> 1, ans = 0;
	if(mid >= l) ans = max(ans, query(l, r, p << 1, cl, mid));
	if(mid < r) ans = max(ans, query(l, r, p << 1 | 1, mid + 1, cr));
	return ans;
}
void update(int l, int r, int d, int p = 1, int cl = 1, int cr = n)
{
	if(cl >= l && cr <= r){
		tree[p] = d;return;// 因为更新是更新成某个值,所以是 = 而不是 +=
	}
	//pushdown(p, cr - cl + 1);
	int mid = (cl + cr) >> 1;
	if(mid >= l) update(l ,r, d, p << 1, cl, mid);
	if(mid < r) update(l, r, d, p << 1 | 1, mid + 1, cr);
	tree[p] = max(tree[p << 1], tree[p << 1 | 1]);
}
void work()
{
    for (int i = 1; i <= n; ++i) scanf("%lld", &A[i]);
    build();
    while(m--)
    {
    	int l, r, d;
    	char c; getchar();
    	scanf("%c %d %d",&c, &l, &r);
    	if(c == 'U') update(l ,l , r);
    	else if(c == 'Q') printf("%lld\n", query(l ,r));
	}
}
int main()
{
    while(~scanf("%lld %lld", &n, &m))
	{
		memset(tree,0,sizeof(tree));
		memset(mark,0,sizeof(mark));
		work();
	}
	return 0;
}

hdu-1698-区间修改成 某个值
题意:每次修改把区间 [ l , r ] [l,r] [l,r] 修改成 d d d,最后查询 [ 1 , n ] [1,n] [1,n] 的和
也就是区间修改,维护区间和

这个题关于 pushdown 的理解,如果是给区间加或减一个数,那么在 pushdown 的时候没必要去管 mark[p] 是否为0(因为写的 += ,如果为0,就是 + 0,无影响),但是如果是修改区间为某个数,那么 pushdown 的前提就是 mark[p] 不为 0 再往下 pushdown.
一句话:以后pushdown加个判断mark[p]是否为0
code:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 9;
ll tree[N << 2], mark[N << 2], n, m, A[N], t, q;
void pushdown(int p, int len)
{
	tree[p << 1] = mark[p] * (len - len / 2);
	mark[p << 1] = mark[p];
	tree[p << 1 | 1] = mark[p] * (len / 2);
	mark[p << 1 | 1] = mark[p];
	mark[p] = 0;
}
void build(int p = 1, int cl = 1, int cr = n)
{
	if(cl == cr) {
		tree[p] = 1; return;
	}
	int mid = (cl + cr) >> 1;
	build(p << 1, cl, mid);
	build(p << 1 | 1, mid + 1, cr);
	tree[p] = tree[p << 1] + tree[p << 1 | 1];
}
ll query(int l,int r, int p = 1, int cl = 1, int cr = n)
{
	if(cl >= l && cr <= r) return tree[p];
	if(mark[p]) pushdown(p, cr - cl + 1);
	ll mid = (cl + cr) >> 1, ans = 0;
	if(mid >= l) ans += query(l, r, p << 1, cl, mid);
	if(mid < r) ans += query(l, r, p << 1 | 1, mid + 1, cr);
	return ans;
}
void update(int l, int r, int d, int p = 1, int cl = 1, int cr = n)
{
	if(cl >= l && cr <= r){
		tree[p] = d * (cr - cl + 1);mark[p] = d;
		return;
	}
	if(mark[p]) pushdown(p, cr - cl + 1);
	int mid = (cl + cr) >> 1;
	if(mid >= l) update(l ,r, d, p << 1, cl, mid);
	if(mid < r) update(l, r, d, p << 1 | 1, mid + 1, cr);
	tree[p] = tree[p << 1] + tree[p << 1 | 1];
}
void work()
{
    //for (int i = 1; i <= n; ++i) A[i] = 1;
    build();
    while(m--)
    {
    	int l, r, d;
    	char c; getchar();
    	scanf("%d %d %d", &l , &r, &d);
    	update(l ,r , d);
	}
	printf("Case %d: The total value of the hook is %lld.\n",++q, query(1, n));
}
int main()
{
	cin >> t;
    while(t--)
	{
		scanf("%lld %lld", &n, &m);
		memset(tree,0,sizeof(tree));
		memset(mark,0,sizeof(mark));
		work();
	}
	return 0;
}

ZOJ-1610-覆盖-求最终每种颜色有多少段
题意:
给定 [ 1 , 8000 ] [1,8000] [18000] 区间,给定n组操作,每次将一段区间修改成某种颜色(对上一次颜色进行覆盖),最后问你能看到多少种颜色,且每种颜色有多少段。
思路:
仔细读题可以发现区间是 左开右闭
查询的时候采取单点查询,用 last=-1,记录下上一次的颜色从1,8000 每个点进行查询如果与上一种颜色不相同,则这种颜色段数++。
题解
code:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 9;
ll tree[N << 2], vis[N << 2], n, last;
void pushdown(int p)
{
	if(tree[p] == -1) return;
	tree[p << 1] = tree[p << 1 | 1] = tree[p];
	tree[p] = -1;
}
void query(int p = 1, int cl = 1, int cr = 8000)
{
	if(cl == cr){
		if(tree[p] != -1 && tree[p] != last) vis[tree[p]]++;

		last = tree[p];//-1也要记录 没有颜色
		return;
	}
	pushdown(p);
	int mid = (cl + cr) >> 1;
	query(p << 1, cl, mid);
	query(p << 1 | 1, mid + 1, cr);
}
void update(int l, int r, int d, int p = 1, int cl = 1, int cr = 8000)
{
	if(cl >= l && cr <= r){
		tree[p] = d;
		return;
	}
	pushdown(p);
	int mid = (cl + cr) >> 1;
	if(mid >= l) update(l ,r, d, p << 1, cl, mid);
	if(mid < r) update(l, r, d, p << 1 | 1, mid + 1, cr);
}
void work()
{
    last = -1;
    while(n--)
    {
    	int l, r, d;
    	scanf("%d %d %d", &l , &r, &d);
    	update(l + 1 , r, d);
	}
	query();
	for(int i = 0; i <= 8000; ++i)
		if(vis[i]) printf("%d %d\n", i, vis[i]);
	printf("\n");
}
int main()
{
    while(~scanf("%lld", &n))
	{
		memset(tree,-1,sizeof(tree));
		memset(vis,0,sizeof(vis));
		work();
	}
	return 0;
}

poj-2777-Count Color
题意:
操作一:把区间 [ l , r ] [l,r] [l,r] 涂上颜色 c ( 一 个 数 字 ) c(一个数字) c()
操作二:查询区间 [ l , r ] [l,r] [l,r] 颜色种类数
题意说种类数最大为30,用二进制每一位表示颜色即可
code:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#define endl '\n'
#define ll long long
using namespace std;
const int maxn = 1e5 + 9;
ll n, m, k;
struct node
{
	int col, sum;
}tr[maxn << 2];

inline void pushup(int p)
{
	tr[p].sum = tr[p << 1].sum | tr[p << 1 | 1].sum;
}
inline void pushdown(int p)
{
	tr[p << 1].col = tr[p].col;
	tr[p << 1 | 1].col = tr[p].col;
	tr[p << 1].sum = tr[p].col;
	tr[p << 1 | 1].sum = tr[p].col;
	tr[p].col = 0;
}
void build(int p = 1, int cl = 1, int cr = n)
{
	if(cl == cr){
		tr[p].col = 0;
		tr[p].sum = 1;return;
	}
	int mid = (cl + cr >> 1);
	build(p << 1, cl, mid);
	build(p << 1 | 1, mid + 1, cr);
	pushup(p);
}
void update(int l, int r, int c, int p = 1, int cl = 1, int cr = n)
{
	if(cl >= l && cr <= r){
		tr[p].col = 1 << (c - 1);
		tr[p].sum = 1 << (c - 1);
		return;
	}
	if(tr[p].col) pushdown(p);
	int mid = (cl + cr) >> 1;
	if(mid >= l) update(l, r, c, p << 1, cl, mid);
	if(mid < r) update(l, r, c, p << 1 | 1, mid + 1, cr);
	pushup(p);
}
int query(int l, int r, int p = 1, int cl = 1, int cr = n)
{
	if(cl >= l && cr <= r) return tr[p].sum;
	if(tr[p].col) pushdown(p);
	int mid = (cl + cr) >> 1, ans1 = 0, ans2 = 0;
	if(mid >= l) ans1 = query(l, r, p << 1, cl, mid);
	if(mid < r) ans2 = query(l, r, p << 1 | 1, mid + 1, cr);
	return (ans1 | ans2);
}
void work()
{
	scanf("%d %d %d", &n, &k, &m);
	build();
	while(m--)
	{
		char op;int l, r, c;
		cin >> op;scanf("%d %d", &l, &r);
		if(l > r) swap(l ,r);
		if(op == 'C'){
			cin >> c;
			update(l, r, c);
		}
		else
		{
			int ans = query(l, r);
			int cnt = 0;
			while(ans){
				if(ans & 1) ++cnt;
				ans >>= 1;
			}
			printf("%d\n", cnt);
		}
	}
}

int main()
{
	//ios::sync_with_stdio(0);
	work();
	return 0;
}

hdu 多校第四场
牛客多校第四场

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值