【luogu CF896C】Willem, Chtholly and Seniorious(珂朵莉树)

123 篇文章 2 订阅

Willem, Chtholly and Seniorious

题目链接:luogu CF896C

题目大意

要你维护一个数组,会有区间加值,区间赋值,求区间第 x 小,求区间每个数的 x 次方和模 y 结果。

思路

这题是珂朵莉树的模板题。

首先珂朵莉树并不是树,它可以说是一种暴力的方法,来解决一种区间赋同一个值然后维护的问题。
(然后根据情况有一些维护需要数据随机有一些又不用)

大概就是你考虑把相同的数合并起来,用一个区间来表示。
然后你改的时候显然要两种操作,合并和分裂。

先说分裂,我们要 [ l , r ] [l,r] [l,r] 变成 [ l , m i d − 1 ] , [ m i d , r ] [l,mid-1],[mid,r] [l,mid1],[mid,r],可以用 lower_bound 找到这个区间先,然后如果 m i d mid mid 就是区间左边 l l l 就不用分裂。
然后是合并,你就通过分裂 r + 1 , l r+1,l r+1,l 得到两边的区间,然后就直接用 erase 进行区间删除把那些都删了,然后加上新的串即可。
(然后注意要先分裂 r + 1 r+1 r+1 再分裂 l l l 否则会可能出现 RE)

然后别的操作大概都是暴力枚举区间做即可。

代码

#include<set>
#include<cstdio>
#include<vector>
#include<algorithm>
#define ll long long
#define mo 1000000007

using namespace std;

ll n, m, seed, vmax, a[100001];

ll rnd (){
	ll re = seed;
	seed = (seed * 7 + 13) % mo;
	return re;
}

struct node {
	ll l, r;
	mutable ll v;
	
	bool operator <(const node &a) const {
		return l < a.l;
	}
};
set <node> s;

set <node> ::iterator split(int pl) {
	set <node> ::iterator it = s.lower_bound((node){pl, 0, 0});
	if (it != s.end() && (*it).l == pl) return it;
	it--;
	if ((*it).r < pl) return s.end();
	ll l = (*it).l, r = (*it).r, v = (*it).v;
	s.erase(it); s.insert((node){l, pl - 1, v});
	return s.insert((node){pl, r, v}).first;
}

//提取区间一定要先分裂右边再分裂左边,不然可能你到到时分裂的后边已经是被删除的,然后就会报错
void assign(ll l, ll r, ll x) {
	set <node> ::iterator itr = split(r + 1), itl = split(l);
	s.erase(itl, itr); s.insert((node){l, r, x});
}

void add(ll l, ll r, ll x) {
	set <node> ::iterator itr = split(r + 1), itl = split(l);
	for (set <node> ::iterator it = itl; it != itr; it++) {
		(*it).v += x;
	}
}

struct Rank {
	ll num, cnt;
	
	bool operator <(const Rank &to) const {
		return num < to.num;
	}
};

ll rnk(ll l, ll r, ll x) {
	set <node> ::iterator itr = split(r + 1), itl = split(l);
	vector <Rank> v;
	for (set <node> ::iterator it = itl; it != itr; it++) {
		v.push_back((Rank){(*it).v, (*it).r - (*it).l + 1});
	}
	sort(v.begin(), v.end());
	for (int i = 0 ; i < v.size(); i++) {
		if (x <= v[i].cnt) return v[i].num;
		x -= v[i].cnt;
	}
}

ll ksm(ll x, ll y, ll p) {
	ll re = 1; x %= p;
	while (y) {
		if (y & 1) re = re * x % p;
		x = x * x % p; y >>= 1;
	}
	return re;
}

ll clacp(ll l, ll r, ll x, ll y) {
	set <node> ::iterator itr = split(r + 1), itl = split(l);
	ll ans = 0;
	for (set <node> ::iterator it = itl; it != itr; it++) {
		ans = (ans + ksm((*it).v, x, y) * ((*it).r - (*it).l + 1) % y) % y;
	}
	return ans;
}

int main() {
	scanf("%lld %lld %lld %lld", &n, &m, &seed, &vmax);
	for (int i = 1; i <= n; i++) {
		a[i] = rnd() % vmax + 1;
		s.insert((node){i, i, a[i]});
	}
	
	for (int i = 1; i <= m; i++) {
		int op = rnd() % 4 + 1; 
		int l = rnd() % n + 1, r = rnd() % n + 1;
		int x, y;
		if (l > r) swap(l, r);
		if (op == 3) {
			x = rnd() % (r - l + 1) + 1;
		}
		else x = rnd() % vmax + 1;
		if (op == 4) y = rnd() % vmax + 1;
		
		if (op == 1) add(l, r, x);
		if (op == 2) assign(l, r, x);
		if (op == 3) printf("%lld\n", rnk(l, r, x));
		if (op == 4) printf("%lld\n", clacp(l, r, x, y));
	}
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值