11.13 模拟赛 T3

题意:给出一个 \(n\) 个点标准的(分界点为 \(\lfloor \frac{l+r}2 \rfloor\))的线段树。定义一个区间的权值为,将这个区间正常地摊开在线段树上访问的结点数。例如,\(n=5,[2, 3]\) 的权值就是 \(5\)\(q\) 组询问,每组给出一个 \([l, r]\),表示询问所有 \([l, r]\) 的非严格子区间(单点也算)的权值和。
\(1 \le n, q \le 5 \times 10 ^ 5\)。强制在线。

既然说了线段树。那就考虑用线段树解决问题。

答案拆分为一些结点的贡献和。
具体地,线段树每个结点维护 \(ans\) 表示当前结点及其所有子结点对答案的贡献。且贡献只考虑不超过当前结点表示的区间的范围

也就是,线段树上的 \([1,2]\)\(ans\) 只表示,\([1, 2], [1, 1], [2, 2]\) 这三个区间的权值和。然后这里的“权值”是只考虑,把这个结点的子树单独拉出来,在这上面访问的。

查询就是,把询问区间摊开到线段树上,合并 \(ans\) 即可。

问题就是,怎么合并 \(ans\)
首先看看线段树上的结点怎么 pushup。
因为一个跨过 \(mid\) 的区间会被拆成一个左儿子的后缀一个右儿子的前缀。所以类似小白逛公园,维护线段树上结点的前缀答案和后缀答案。
pushup 是容易的。注意因为是 pushup,所以如果要求一个父结点的权值的话,按道理来说是 \(1\),但是下面可能会算多。要小心。

然后 query 的合并不需要注意这一点。正常合并即可。

时间复杂度 \(O(n+q\log n)\)

#include <bits/stdc++.h>
using namespace std;

//#define filename "ran" 
#define FileOperations() freopen(filename".in", "r", stdin), freopen(filename".out", "w", stdout)
//#define multi_cases 1

#define inf 0x3f3f3f3f
#define Linf 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int> 
#define ull unsigned long long
#define all(v) v.begin(), v.end()
#define upw(i, a, b) for(int i = (a); i <= (b); ++i)
#define dnw(i, a, b) for(int i = (a); i >= (b); --i)

template<class T> bool vmax(T &a, T b) { return b > a ? a = b, true : false; }
template<class T> bool vmin(T &a, T b) { return b < a ? a = b, true : false; }
template<class T> void clear(T &x) { T().swap(x); }

const int N = 5e5+2;

#define int long long

int n, q, type;

struct Data {
	int ans, lans, rans;
	int len;
};

Data merge1(const Data &a, const Data &b) {
	int len = a.len + b.len;
	int ans = a.ans + b.ans + (1+a.rans) * b.len - b.len - 1 + (1+b.lans) * a.len - a.len - 1;
	int lans = a.lans + b.lans + b.len - 2;
	int rans = a.rans + b.rans + a.len - 2;
	return {ans, lans, rans, len};
}
Data operator + (const Data &a, const int &b) {
	return {a.ans + b * (b+1) / 2, a.lans + b, a.rans + b, a.len};
}
Data merge2(const Data &a, const Data &b) {
	int len = a.len + b.len;
	int ans = a.ans + b.ans + a.rans * b.len + b.lans * a.len;	//非结点上的合并不需要去除 并区间 的贡献
	int lans = a.lans + b.lans + b.len;
	int rans = a.rans + b.rans + a.len;
	return {ans, lans, rans, len};
}

struct node {
	node *l, *r;
	Data d;
	void up() { d = merge1(l->d, r->d); }
} pool[N << 1], *tmp = pool;

node *newnode() {
	tmp->l = tmp->r = NULL;
	tmp->d = {1, 1, 1, 1};
	return tmp++;
}

struct Sgt {
	node *root;
	int l, r;
	void build(node *&p, int l, int r) {
		p = newnode();
		if(l == r) return;
		int mid = l + r >> 1;
		build(p->l, l, mid), build(p->r, mid+1, r), p->up(), p->d = p->d + (r-l+1);
	} void build(int l, int r) { build(root, l, r), this->l = l, this->r = r; }
	
	Data query(node *p, int l, int r, int ql, int qr) {
		if(l == ql && r == qr) return p->d;
		int mid = l + r >> 1;
		if(mid >= qr) return query(p->l, l, mid, ql, qr) + (qr - ql + 1);
		if(mid < ql) return query(p->r, mid+1, r, ql, qr) + (qr - ql + 1);
		return merge2(query(p->l, l, mid, ql, mid), query(p->r, mid+1, r, mid+1, qr)) + (qr - ql + 1);
	} int query(int ql, int qr) { return query(root, l, r, ql, qr).ans; }
} tr;

void WaterM() {
	cin >> n >> q >> type;
	tr.build(1, n);
	int lastans = 0;
	// tr.traverse();
	while(q--) {
		int l, r;
		scanf("%lld%lld", &l, &r);
		l = (l ^ (lastans * type)) % n + 1;
		r = (r ^ (lastans * type)) % n + 1;
		if(l > r) swap(l, r);
		// cerr << l << ' ' << r << '\n';
		printf("%lld\n", lastans = tr.query(l, r));
	}
}

signed main() {
#ifdef filename
	FileOperations();
#endif
	
	signed _ = 1;
#ifdef multi_cases
	scanf("%d", &_);
#endif
	while(_--) WaterM();
	return 0;
}


posted @ 2025-11-13 20:06  Water_M  阅读(12)  评论(0)    收藏  举报