二进制【线段树】

>Link

luogu U137918


>Description

在这里插入图片描述
对于100%的数据,01串长度<=200000,N<=200000


>解题思路

对二进制数的一段进行排序的话,排出来的那一段就是形如 0000011111 或 11111100000
那我们只要知道这一段的1的个数(0的个数也可以),然后把这一段分成两个区间,分别对两个区间里所有的值赋值
然后这不就是线段树吗(麻了,真不知道我比赛的时候为什么妹想到QAQ

还有一个操作是对二进制的一段取值,那我们也可以在线段树上维护,把左儿子乘上2的右儿子的长度的次方,然后再加上右儿子就行了

改题的时候不知道为什么一直超时,后来才发现是询问的时候打错了return的条件,一直询问到了叶子节点,还是太粗心了QAQ


>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <ctime>
#define LL long long
#define N 200010
using namespace std;

const LL Mod = 1e9 + 7;
struct node
{
	LL vl; int ln;
};
int n, a[N], sum[N * 4], lazy[N * 4], len[N * 4];
LL power[N], val[N * 4];
string s;

void build (int k, int l, int r)
{
	len[k] = r - l + 1;
	if (l == r)
	{
		sum[k] = val[k] = a[l];
		return;
	}
	int mid = (l + r) / 2;
	build (k * 2, l, mid);
	build (k * 2 + 1, mid + 1, r);
	sum[k] = sum[k * 2] + sum[k * 2 + 1];
	val[k] = (val[k * 2] * power[len[k * 2 + 1]] % Mod + val[k * 2 + 1]) % Mod;
}
void pushdown (int k)
{
	if (lazy[k] == -1) return;
	sum[k] = lazy[k] * len[k];
	if (!lazy[k]) val[k] = 0;
	else val[k] = (power[len[k]] - 1 + Mod) % Mod;
	lazy[k * 2] = lazy[k * 2 + 1] = lazy[k];
	lazy[k] = -1;
}
int asksum (int k, int l, int r, int ll, int rr)
{
	pushdown (k);
	if (ll <= l && r <= rr) return sum[k];
	int mid = (l + r) / 2, ret = 0;
	if (ll <= mid) ret += asksum (k * 2, l, mid, ll, rr);
	if (rr >= mid + 1) ret += asksum (k * 2 + 1, mid + 1, r, ll, rr);
	return ret;
}
void change (int k, int l, int r, int ll, int rr, int x)
{
	if (ll <= l && r <= rr)
	{
		lazy[k] = x;
		pushdown (k);
		return;
	}
	pushdown (k);
	pushdown (k * 2), pushdown (k * 2 + 1);
	int mid = (l + r) / 2;
	if (ll <= mid) change (k * 2, l, mid, ll, rr, x);
	if (rr >= mid + 1) change (k * 2 + 1, mid + 1, r, ll, rr, x);
	sum[k] = sum[k * 2] + sum[k * 2 + 1];
	val[k] = (val[k * 2] * power[len[k * 2 + 1]] % Mod + val[k * 2 + 1]) % Mod;
}
node askval (int k, int l, int r, int ll, int rr)
//询问的区间不一定就包含整个节点,我们又要知道每个节点的要取的长度,所以要返回两个值
{
	pushdown (k);
	if (ll <= l && r <= rr) return (node){val[k], len[k]};
	int mid = (l + r) / 2;
	node p1 = (node){0, 0}, p2 = (node){0, 0}, ret;
	if (ll <= mid) p1 = askval (k * 2, l, mid, ll, rr);
	if (rr >= mid + 1) p2 = askval (k * 2 + 1, mid + 1, r, ll, rr);
	ret.vl = (p1.vl * power[p2.ln] % Mod + p2.vl) % Mod;
	ret.ln = p1.ln + p2.ln;
	return ret;
}

int main()
{
	memset (lazy, -1, sizeof (lazy));
	int Q, opt, l, r, tot;
	cin >> s;
	n = s.size(), s = " " + s;
	for (int i = 1; i <= n; i++) a[i] = s[i] - '0';
	power[0] = 1;
	for (int i = 1; i <= n; i++) power[i] = power[i - 1] * 2 % Mod;
	build (1, 1, n);
	scanf ("%d", &Q);
	while (Q--)
	{
		scanf ("%d%d%d", &opt, &l, &r);
		if (opt == 1)
		{
			tot = asksum (1, 1, n, l, r);
			change (1, 1, n, l, l + tot - 1, 1);
			change (1, 1, n, l + tot, r, 0);
		}
		else if (opt == 2)
		{
			tot = asksum (1, 1, n, l, r);
			change (1, 1, n, l, r - tot, 0);
			change (1, 1, n, r - tot + 1, r, 1);
		}
		else printf ("%lld\n", askval (1, 1, n, l, r).vl);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值