// // // // // // // // // // // // // //

腾飞营_数据结构

数据结构

前言

本来是想只放题目的 然后题目不会做 于是就爬了

又搞了一个扔知识点的地方 希望有点用

芝士

ODT

众所周知 OTD 是一种暴力

珂朵莉树又叫老司机树 一种基于 set 实现的用来骗分的数据结构(不确定这算不算是一种数据结构

这东西在有区间平推操作且出题人不好好造数据的情况下可以平推数据结构题

set

set 的本质是一棵平衡树(红黑树) 会对插入其中的元素进行去重与排序

常用函数

set <数据类型> t;
t.begin(); //返回指向第一个元素的迭代器(地址)
t.end();
t.clear(); 
t.insert(x);
t.empty();
t.size();
t.count(x); //返回键为 x 的元素数量
t.find(x); //存在 x 时返回迭代器 否则返回 end()
t.erase(x); //删除值为 x 的所有元素 返回删除元素的个数
t.erase(l, r); //删除迭代器在区间 [l, r) 中所有元素
t.lower_bound(x); //查找 x 在 t 中的位置 返回一个迭代器 若不存在 返回 end()
t.upper_bound(x);
set <数据类型> ::iterator pos; //定义一个迭代器

构造

不是树的树

set 中每一个节点由一个三元组构成 \((l, r, val)\) 表示区间 \([l, r]\) 中的元素全为 \(val\)

代码

struct node {
	int l, r; mutable int val;
	node(int L, int R = 0, int V = 0) : l(L), r(R), val(V) {}
	bool operator < (const node &x) const {return l < x.l;}
};
std::set <node> t;

分裂

分裂是珂朵莉树的核心操作 其所有对于区间的操作都是基于分裂实现的 传入一个参数 \(pos\) 将区间分为 \([l, pos)\)\([pos, r)\) 所以一般右边传入的都是 \(r + 1\)

代码

IT split(int pos) {
	IT it = t.lower_bound(node(pos));
	if(it != t.end() && it -> l == pos) return it;
	it--; if(it -> r < pos) return t.end();
	int l = it -> l, r = it -> r, val = it -> val;
	t.erase(it); t.insert(node(l, pos - 1, val));
	return t.insert(node(pos, r, val)).first;
}

推平

一种可以保证 set 复杂度的操作 当数据随机时 抽到该操作的概率比较大 就可以有效的减少 set 中的点的个数 来降低 set 的复杂度

代码

void assign(int l, int r, int val) {
	IT itr = split(r + 1), itl = split(l);
	t.erase(itl, itr); t.insert(node(l, r, val));
}

其他操作

把区间分裂出来 挨个处理(就是暴力)


题目

P5482 不等式组

恶心

对于不等式

\[ax + b > c \]

\(a > 0\) 时 有

\[a > \frac {c - b}a \]

\(a = 0\) 时 有

\[0 > c - b \]

\(a < 0\) 时 有

\[a < \frac {c - b}a \]

对于给定整数 \(k\) 相当于在 \(a > 0\) 的不等式中查询有多少数小于 \(k\)\(a < 0\) 的不等式中有多少数大于 \(k\) (先不考虑 \(a = 0\))

显然可以通过树状数组解决

但是分式可能会出现浮点数 看到题解里面好多都是写的上下取整 不是很清楚为什么是对的 所以我写的离散化

将操作离线 离散化之后建立两个树状数组 就可以做了

对于 \(a = 0\) 的情况 直接特判记录答案最后加上就可以了

但是离线会出很多稀奇古怪的问题(至少是我遇到的 可能是写的假)

几个容易挂的地方

  1. 所有的数包括询问都要离散化

  2. \(a = 0\) 的情况要特别处理 加入删除都要注意

  3. 删掉的不等式可能会多次被删 (下了个数据才发现)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define pn putchar('\n')
#define lb(x) ((x) & -(x))
/*-----------------------------------------------------------------------*/
const int B = 1e5 + 7;
/*-----------------------------------------------------------------------*/
int n, t1[B << 1], t2[B << 1], ans[B], cnt, _cnt;
double c[B], _c[B];
bool vis[B], p1[B], p2[B], p3[B], p4[B], p5[B];
struct node {double _d, _del; int d, del, qur; bool p;} a[B];
char s[10];
/*-----------------------------------------------------------------------*/
inline int read() {
	int x = 0, f = 0; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = 1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return f ? -x : x;
}
void print(int x) {if(x > 9) print(x / 10); putchar(x % 10 ^ 48);}
void Print(int x) {if(x < 0) putchar('-'), x = -x; print(x);}
/*-----------------------------------------------------------------------*/
void add(int x, int k, int *t) {for(int i = x; i <= n; i += lb(i)) t[i] += k;}
int sum(int x, int *t) {int res = 0; for(int i = x; i; i -= lb(i)) res += t[i]; return res;}
/*-----------------------------------------------------------------------*/
int main() {
	n = read();
	for(int i = 1; i ^ n + 1; i++)
	{
		scanf("%s", s + 1); ans[i] = ans[i - 1];
		if(s[1] == 'A')
		{
			int x = read(), y = read(), z = read();
			if(x > 0) _c[++_cnt] = c[++cnt] = a[i]._d = 1.0 * (z - y) / x, p1[i] = 1;
			else if(x == 0) {cnt++; if(y > z) ans[i]++, c[cnt] = 1e8; p4[cnt] = 1;}
			else if(x < 0) _c[++_cnt] = c[++cnt] = a[i]._d = 1.0 * (z - y) / x, vis[cnt] = a[i].p = p1[i] = 1;
		}
		else if(s[1] == 'D')
		{
			int x = read(); if(p5[x]) continue; p5[x] = 1;
			if(p4[x]) {if(c[x] == 1e8) ans[i]--; continue;}
			a[i]._del = c[x]; p2[i] = 1;
			if(vis[x]) a[i].p = 1;
		}
		else if(s[1] == 'Q') p3[i] = 1, _c[++_cnt] = a[i].qur = read();
	}
	std::sort(_c + 1, _c + 1 + _cnt); _cnt = std::unique(_c + 1, _c + 1 + _cnt) - _c - 1;
	for(int i = 1; i ^ n + 1; i++)
		if(p1[i]) a[i].d = std::lower_bound(_c + 1, _c + 1 + _cnt, a[i]._d) - _c;
		else if(p2[i]) a[i].del = std::lower_bound(_c + 1, _c + 1 + _cnt, a[i]._del) - _c;
		else if(p3[i]) a[i].qur = std::lower_bound(_c + 1, _c + 1 + _cnt, a[i].qur) - _c;
	for(int i = 1; i ^ n + 1; i++)
	{
		if(p1[i]) if(a[i].p) add(a[i].d, 1, t2); else add(a[i].d, 1, t1);
		else if(p2[i]) if(a[i].p) add(a[i].del, -1, t2); else add(a[i].del, -1, t1);
		else if(p3[i])
		{
			int res = sum(a[i].qur - 1, t1);
			res += sum(n + 1, t2) - sum(a[i].qur, t2);
			Print(ans[i] + res); pn;
		}
	}
	return 0;
}


P1972 HH的项链

这是一个悲伤的故事

当初三月份 写主席树被卡 写到 \(79\) 分过不去

现在七月份 写莫队再次被卡 写到 \(72\) 分过不去

行吧 拿着复杂度不对的办法来写是我的不对 不过骗分还是不错的

正解

树状数组

对于一段区间 \([1, r]\) 所有相同数中有贡献的只有最后一个

将询问离线 按照询问的 \(r\) 从小到大排序 每到一个 \(r\) 的时候更新相应的询问即可

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define pn putchar('\n')
#define lb(x) ((x) & -(x))
/*-----------------------------------------------------------------------*/
const int C = 1e6 + 7;
/*-----------------------------------------------------------------------*/
int n, m, t[C], pre[C], a[C], ans[C];
struct Query {int l, r, id;} q[C];
/*-----------------------------------------------------------------------*/
inline int read() {
	int x = 0, f = 0; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = 1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return f ? -x : x;
}
void print(int x) {if(x > 9) print(x / 10); putchar(x % 10 ^ 48);}
void Print(int x) {if(x < 0) putchar('-'), x = -x; print(x);}
/*-----------------------------------------------------------------------*/
bool cmp(Query x, Query y) {return x.r < y.r;}
void add(int x, int k) {for(int i = x; i <= n; i += lb(i)) t[i] += k;}
int sum(int x) {int res = 0; for(int i = x; i; i -= lb(i)) res += t[i]; return res;}
/*-----------------------------------------------------------------------*/
int main() {
	n = read();
	for(int i = 1; i ^ n + 1; i++) a[i] = read();
	m = read();
	for(int i = 1; i ^ m + 1; i++) q[i] = (Query){read(), read(), i};
	std::sort(q + 1, q + 1 + m, cmp);
	for(int i = 1, j = 1; i ^ n + 1 || j ^ m + 1; i++)
	{
		add(i, 1); if(!pre[a[i]]) pre[a[i]] = i; else add(pre[a[i]], -1), pre[a[i]] = i;
		while(i == q[j].r) ans[q[j].id] = sum(q[j].r) - sum(q[j].l - 1), j++;
	}
	for(int i = 1; i ^ m + 1; i++) Print(ans[i]), pn;
	return 0;
}

P2023 维护序列

线段树模板题 不再展开


P4198 楼房重建

线段树维护单调递增的斜率

维护区间最大值 向上合并的时候对左右区间的最大值进行分类讨论 递归合并 做到复杂度 \(O(\log n)\) 即可通过

其实我第一遍写了一棵每次操作操作 \(O(n)\) 的线段树

#include<cstdio>
#include<cstring>
#define pt putchar(' ')
#define pn putchar('\n')
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*---------------------------------------------------------------------*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
/*---------------------------------------------------------------------*/
void Fire() {
	freopen(".in", "r", stdin);
	freopen(".out", "w", stdout);
}
/*---------------------------------------------------------------------*/
int n, m;
/*---------------------------------------------------------------------*/
inline int read() {
	int x = 0, f = 0; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = 1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return f ? -x : x;
}
void print(int x) {if(x > 9) print(x / 10); putchar(x % 10 ^ 48);}
void Print(int x) {if(x < 0) putchar('-'), x = -x; print(x);}
/*---------------------------------------------------------------------*/
namespace Seg {
	#define ls(x) x << 1
	#define rs(x) x << 1 | 1
	#define mid (t[p].l + t[p].r >> 1)
	#define Check (pos == t[p].l && t[p].r == pos)
	struct node {int l, r, ans; double max;} t[B << 2];
	void build(int p, int l, int r) {
		t[p].l = l; t[p].r = r; if(l == r) return ;
		build(ls(p), l, mid); build(rs(p), mid + 1, r);
	}
	void push_up1(int p) {t[p].max = Max(t[ls(p)].max, t[rs(p)].max);}
	int push_up2(int p, double lim) {
		if(t[p].l == t[p].r) return 1;
		if(t[p].r - t[p].l == 1)
		{
			if(lim >= t[p].max) return 0;
			if(t[ls(p)].max >= t[rs(p)].max) return 1;
			if(t[ls(p)].max <= lim) return 1;
			return 2;
		}
		if(t[ls(p)].max >= t[rs(p)].max) return push_up2(ls(p), lim);
		if(t[ls(p)].max <= lim) return push_up2(rs(p), lim);
		return push_up2(ls(p), lim) + t[p].ans - t[ls(p)].ans;
	}
	void up_date(int p, int pos, double k) {
		if(Check) {t[p].max = k; t[p].ans = 1; return ;}
		if(pos <= mid) up_date(ls(p), pos, k); else up_date(rs(p), pos, k);
		push_up1(p);
		if(t[ls(p)].max >= t[rs(p)].max) t[p].ans = t[ls(p)].ans;
		else t[p].ans = t[ls(p)].ans + push_up2(rs(p), t[ls(p)].max);
	}
}
/*---------------------------------------------------------------------*/
int main() {
	n = read(); m = read(); Seg::build(1, 1, n);
	for(int i = 1; i ^ m + 1; i++)
	{
		int x = read(), y = read();
		Seg::up_date(1, x, y * 1.0 / x);
		Print(Seg::t[1].ans); pn;
	}
	return 0;
}


P5278 算术天才⑨与等差数列

维护一些乱七八糟的东西 只要不被卡掉就能过 比如最大值 最小值 区间和 区间异或和 区间平方和...

但是由于数据太水 只维护了一个最大值最小值和区间和就过了

/*
  Source: P5278 算术天才⑨与等差数列
  大概就是把能维护的都维护一下 出题人不知道维护的什么就卡不掉了
  先尝试最大最小值以及区间和 
*/
#include<cstdio>
#include<cstring>
#define pt putchar(' ')
#define pn putchar('\n')
#define int long long
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int B = 3e5 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
	freopen(".in", "r", stdin);
	freopen(".out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, m, a[B], cnt;
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void print(int x) {if(x > 9) print(x / 10); putchar(x % 10 ^ 48);}
void Print(int x) {if(x < 0) putchar('-'), x = -x; print(x);}
/*----------------------------------------------------------*/
namespace Seg {
	#define ls(x) x << 1
	#define rs(x) x << 1 | 1
	#define mid (t[p].l + t[p].r >> 1)
	struct node {int l, r, sum, max, min;} t[B << 2];
	node operator + (node x, node y) {
		node z; z.l = x.l; z.r = y.r; z.sum = x.sum + y.sum;
		z.max = Max(x.max, y.max); z.min = Min(x.min, y.min);
		return z;
	}
	void build(int p, int l, int r) {
		t[p].l = l; t[p].r = r;
		if(l == r) {t[p].sum = t[p].max = t[p].min = a[l]; return ;}
		build(ls(p), l, mid); build(rs(p), mid + 1, r);
		t[p] = t[ls(p)] + t[rs(p)];
	}
	void up_date(int p, int pos, int k) {
		if(t[p].l == pos && pos == t[p].r) {t[p].sum = t[p].max = t[p].min = k; return ;}
		if(pos <= mid) up_date(ls(p), pos, k); else up_date(rs(p), pos, k);
		t[p] = t[ls(p)] + t[rs(p)];
	}
	node query(int p, int l, int r) {
		if(l <= t[p].l && t[p].r <= r) return t[p];
		if(l <= mid) if(r > mid) return query(ls(p), l, r) + query(rs(p), l, r);
		else return query(ls(p), l, r); else return query(rs(p), l, r);
	}
}
/*----------------------------------------------------------*/
signed main() {
	n = read(); m = read();
	for(int i = 1; i ^ n + 1; i++) a[i] = read();
	Seg::build(1, 1, n);
	for(int i = 1; i ^ m + 1; i++)
		if(read() == 1)
		{
			int x = read() ^ cnt, y = read() ^ cnt;
			Seg::up_date(1, x, y);
		}
		else
		{
			int l = read() ^ cnt, r = read() ^ cnt, k = read() ^ cnt;
			Seg::node tmp = Seg::query(1, l, r);
			if(tmp.max - tmp.min != (r - l) * k) {puts("No"); continue;}
			if(tmp.sum != (tmp.min + tmp.max) * (r - l + 1) / 2) {puts("No"); continue;}
			puts("Yes"); cnt++;
		}
	return 0;
}

P3792 由乃与大母神原型和偶像崇拜

据说是上一个题的弱化版 但是实际上数据稍微强一点

维护区间最值以及区间和还有区间平方和就过了

/*
  Source: P3792 由乃与大母神原型和偶像崇拜
  多维护一些稀奇古怪的东西 卡不掉就行了 
  尝试维护最大值 最小值 异或和 
  然后 64 分  所以再加上区间和 
  这个异或有点占空间 再加上区间平方和 
*/
#include<cstdio>
#include<cstring>
#define pt putchar(' ')
#define pn putchar('\n')
#define ll unsigned long long 
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int B = 5e5 + 7;
const int D = 2.5e7 + 7;
const int mod1 = 1e9 + 7;
const int mod2 = 1e9 + 9;
const int inv1 = 500000004;
const int inv2 = 833333341;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
int n, m, a[B];
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void print(int x) {if(x > 9) print(x / 10); putchar(x % 10 ^ 48);}
void Print(int x) {if(x < 0) putchar('-'), x = -x; print(x);}
/*----------------------------------------------------------*/
namespace Seg {
	#define ls(x) x << 1
	#define rs(x) x << 1 | 1
	#define mid (t[p].l + t[p].r >> 1)
	struct node {int l, r, max, min; ll sum, Sum;} t[B << 2];
	node operator + (node x, node y) {
		node z; z.l = x.l; z.r = y.r; 
		z.max = Max(x.max, y.max); z.min = Min(x.min, y.min);
		z.sum = (x.sum + y.sum) % mod1; z.Sum = (x.Sum + y.Sum) % mod2;
		return z;
	}
	void f(int p, int k) {
		t[p].max = t[p].min = k; t[p].sum = k % mod1;
		t[p].Sum = 1ll * k * k % mod2;
	}
	void build(int p, int l, int r) {
		t[p].l = l; t[p].r = r; if(l == r) {f(p, a[l]); return ;}
		build(ls(p), l, mid); build(rs(p), mid + 1, r);
		t[p] = t[ls(p)] + t[rs(p)];
	}
	void up_date(int p, int pos, int k) {
		if(t[p].l == pos && pos == t[p].r) {f(p, k); return ;}
		if(pos <= mid) up_date(ls(p), pos, k); else up_date(rs(p), pos, k);
		t[p] = t[ls(p)] + t[rs(p)];
	}
	node query(int p, int l, int r) {
		if(l <= t[p].l && t[p].r <= r) return t[p];
		if(l <= mid) if(r > mid) return query(ls(p), l, r) + query(rs(p), l, r);
		else return query(ls(p), l, r); else return query(rs(p), l, r);
	}
}
/*----------------------------------------------------------*/
signed main() {
	n = read(); m = read();
	for(int i = 1; i ^ n + 1; i++) a[i] = read();
	Seg::build(1, 1, n);
	for(int i = 1; i ^ m + 1; i++)
		if(read() == 1) {int x = read(), y = read(); Seg::up_date(1, x, y);}
		else
		{
			int l = read(), r = read();
			Seg::node tmp = Seg::query(1, l, r);
			if(tmp.max - tmp.min != r - l) {puts("yuanxing"); continue;}
			if(tmp.sum != 1ll * (tmp.min + tmp.max) % mod1 * (r - l + 1) % mod1 * inv1 % mod1) {puts("yuanxing"); continue;}
			if(tmp.Sum != (1ll * tmp.max * (tmp.max + 1) % mod2 * (2 * tmp.max + 1) % mod2 * inv2 % mod2 - 1ll * (tmp.min - 1) * tmp.min % mod2 * (2 * tmp.min - 1) % mod2 * inv2 % mod2 + mod2) % mod2) {puts("yuanxing"); continue;}
			puts("damushen");
		}
	return 0;
}

P3586 LOG

如果一个数大于 \(s\) 每次拿这个去减是一定不亏的

如果小于 \(s\) 就需要几个数凑一下 上面统计出大于等于 \(s\) 的数 记为 \(x\)

如果小于 \(s\) 的数的和 记为 \(sum < (c - x) \times s\) 那么一定是不行的 反之则可以

因为 这些数都是小于 \(s\) 的 它们的和又是大于 \(s\) 的 那么一定有多于 \(c - x\) 个 匀一下就可以了

这里求大于等于 \(s\) 的数的数量以及小于 \(s\) 的数的和 都可以将操作离线 离散化之后通过树状数组来求

吸氧卡到最优解 (2021.7.22数据)

/*
  Source: P3586 [POI2015]LOG
  如果一个数大于 s 每次拿这个去减是一定不亏的
  如果小于 s 就需要几个数凑一下 上面统计出大于 s 的数 记为 x 
  如果小于 s 的数的和 记为 sum < (c - x) * s 那么一定是不行的 反之则可以
  因为 这些数都是小于 s 的 它们的和又是大于 s 的 那么一定有多于 c - x 个 匀一下就可以了 
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define lb(x) ((x) & -(x))
/*----------------------------------------------------------*/
const int C = 1e6 + 7;
/*----------------------------------------------------------*/
inline void File() {
	freopen(".in", "r", stdin);
	freopen(".out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, m, a[C], b[C], d[C], tot;
ll t1[C], t2[C];
struct Query {int c, s, y; bool p;} q[C];
char s[2];
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void print(int x) {if(x > 9) print(x / 10); putchar(x % 10 ^ 48);}
void Print(int x) {if(x < 0) putchar('-'), x = -x; print(x);}
/*----------------------------------------------------------*/
void add(ll *t, int x, int k) {for(int i = x; i <= tot; i += lb(i)) t[i] += k;}
ll sum(ll *t, int x) {ll res = 0; for(int i = x; i; i -= lb(i)) res += t[i]; return res;}
/*----------------------------------------------------------*/
int main() {
	n = read(); m = read();
	for(int i = 1; i ^ m + 1; i++)
	{
		scanf("%s", s + 1);
		if(s[1] == 'U') q[i] = (Query){read(), read(), 0, 0};
		if(s[1] == 'Z') q[i] = (Query){read(), read(), 0, 1};
		d[++tot] = q[i].s;
	}
	std::sort(d + 1, d + 1 + tot); tot = std::unique(d + 1, d + 1 + tot) - d - 1;
	for(int i = 1; i ^ m + 1; i++) q[i].y = std::lower_bound(d + 1, d + 1 + tot, q[i].s) - d;
	for(int i = 1; i ^ m + 1; i++)
		if(q[i].p)
		{
			ll Cnt = sum(t1, tot) - sum(t1, q[i].y - 1), Sum = sum(t2, q[i].y - 1);
			if(Sum >= (q[i].c - Cnt) * q[i].s) puts("TAK"); else puts("NIE");
		}
		else
		{
			if(a[q[i].c]) add(t1, b[q[i].c], -1), add(t2, b[q[i].c], -a[q[i].c]);
			add(t1, q[i].y, 1); add(t2, q[i].y, q[i].s); a[q[i].c] = q[i].s; b[q[i].c] = q[i].y;
		}
	return 0;
}

P4219 大融合

离线 先把树建好 实际上是森林 通过并查集维护加边

用并查集维护每个连通块中深度最浅的点 连边的时候相当于链加 询问的时候查询某个点的孩子数

通过差分转换为单点修改 子树查询 通过树状数组 + dfs 序维护即可

/*
  Source: P4219 [BJOI2014]大融合
*/
#include<cstdio>
#include<cstring>
#define pn putchar('\n')
#define lb(x) ((x) & -(x))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int B = 1e5 + 7;
/*----------------------------------------------------------*/
int n, m, fa[B], f[B], dep[B], siz[B], kcnt, l[B], r[B], t[B];
struct Query {int x, y; bool p;} q[B];
struct edge {int v, nxt;} e[B << 1];
int head[B], ecnt;
char s[3];
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void add_edge(int u, int v) {e[++ecnt] = (edge){v, head[u]}; head[u] = ecnt;}
void add(int x, int k) {if(!x) return ; for(int i = x; i <= n; i += lb(i)) t[i] += k;}
int sum(int x) {int res = 0; for(int i = x; i; i -= lb(i)) res += t[i]; return res;}
int find(int x) {return x == f[x] ? x : f[x] = find(f[x]);}
void dfs(int u, int pre) {
	dep[u] = dep[pre] + 1; l[u] = ++kcnt; fa[u] = pre;
	for(int i = head[u], v; i; i = e[i].nxt) if(!l[v = e[i].v]) dfs(v, u);
	r[u] = kcnt;
}
/*----------------------------------------------------------*/
int main() {
	n = read(); m = read();
	for(int i = 1; i ^ m + 1; i++)
	{
		scanf("%s", s + 1); int x = read(), y = read();
		if(s[1] == 'Q') q[i] = (Query){x, y, 0};
		else q[i] = (Query){x, y, 1}, add_edge(x, y), add_edge(y, x);
	}
	for(int i = 1; i ^ n + 1; i++) if(!l[i]) dfs(i, 0);
	for(int i = 1; i ^ n + 1; i++) f[i] = i, add(l[i], 1), add(l[fa[i]], -1);
	for(int i = 1; i ^ m + 1; i++)
	{
		int u = q[i].x, v = q[i].y, x = find(u), y = find(v);
		if(l[u] > l[v]) Swap(u, v), Swap(x, y);
		int tmp1 = sum(r[v]) - sum(l[v] - 1), tmp2 = sum(r[x]) - sum(l[x] - 1);
		if(q[i].p) add(l[u], tmp1), add(l[fa[x]], -tmp1), f[v] = x;
		else Print(tmp1 * (tmp2 - tmp1)), pn;
	}
	return 0;
}

P4211 LCA

题目要求

\[\sum_{i = l}^rdep[LCA(i, z)] \]

差分

\[\sum_{i = 1}^rdep[LCA(i, z)] - \sum_{i = 1}^{l - 1}dep[LCA(i, z)] \]

考虑如何求

\[\sum_{i = 1}^ydep[LCA(i, x)] \]

\(1 \to u\) 的路径加一 求 \(1 \to v\) 路径和即为 \(dep[LCA(u, v)]\)

这样就可以通过树剖解决了 细节见代码

/*
  Source: P4211 [LNOI2014]LCA
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#define pt putchar(' ')
#define pn putchar('\n')
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int A = 5e4 + 7;
const int mod = 201314;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
	freopen(".in", "r", stdin);
	freopen(".out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, m, qcnt;
struct Query {int x, z, id, ans;} q[A];
struct edge {int v, nxt;} e[A << 1];
int head[A], ecnt;
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void add_edge(int u, int v) {e[++ecnt] = (edge){v, head[u]}; head[u] = ecnt;}
bool cmp1(Query x, Query y) {return x.x == y.x ? x.z < y.z : x.x < y.x;}
bool cmp2(Query x, Query y) {return x.id == y.id ? x.x < y.x : x.id < y.id;}
namespace Seg {
	#define ls(x) x << 1
	#define rs(x) x << 1 | 1
	#define mid (t[p].l + t[p].r >> 1)
	struct node {int l, r, len, sum, lzy;} t[A << 2];
	void push_up(int p) {t[p].sum = t[ls(p)].sum + t[rs(p)].sum;}
	void build(int p, int l, int r) {
		t[p].l = l; t[p].r = r; t[p].len = r - l + 1;
		if(l == r) return ;
		build(ls(p), l, mid); build(rs(p), mid + 1, r); push_up(p);
	}
	void f(int p, int k) {(t[p].sum += t[p].len * k) %= mod; (t[p].lzy += k) %= mod;}
	void push_down(int p) {f(ls(p), t[p].lzy); f(rs(p), t[p].lzy); t[p].lzy = 0;}
	void up_date(int p, int l, int r, int k) {
		if(l <= t[p].l && t[p].r <= r) {f(p, k); return ;}
		if(t[p].lzy) push_down(p);
		if(l <= mid) up_date(ls(p), l, r, k);
		if(r > mid) up_date(rs(p), l, r, k);
		push_up(p);
	}
	int query(int p, int l, int r) {
		if(l <= t[p].l && t[p].r <= r) return t[p].sum;
		if(t[p].lzy) push_down(p); int res = 0;
		if(l <= mid) (res += query(ls(p), l, r)) %= mod;
		if(r > mid) (res += query(rs(p), l, r)) %= mod;
		return res;
	}
}
namespace Cut {
	int fa[A], dfn[A], siz[A], son[A], dep[A], top[A], kcnt;
	void dfs1(int u, int pre) {
		dep[u] = dep[pre] + 1; fa[u] = pre; siz[u] = 1;
		for(int i = head[u]; i; i = e[i].nxt)
		{
			int v = e[i].v; if(v == pre) continue;
			dfs1(v, u); siz[u] += siz[v];
			if(siz[v] > siz[son[u]]) son[u] = v;
		}
	}
	void dfs2(int u, int tp) {
		dfn[u] = ++kcnt; top[u] = tp;
		if(!son[u]) return ; dfs2(son[u], tp);
		for(int i = head[u]; i; i = e[i].nxt)
		{
			int v = e[i].v;
			if(v == fa[u] || v == son[u]) continue;
			dfs2(v, v);
		}
	}
	void up_date(int x, int y, int k) {
		while(top[x] != top[y])
		{
			if(dep[top[x]] < dep[top[y]]) Swap(x, y);
			Seg::up_date(1, dfn[top[x]], dfn[x], k);
			x = fa[top[x]];
		}
		if(dfn[x] > dfn[y]) Swap(x, y);
		Seg::up_date(1, dfn[x], dfn[y], k);
	}
	int query(int x, int y) {
		int res = 0;
		while(top[x] != top[y])
		{
			if(dep[top[x]] < dep[top[y]]) Swap(x, y);
			(res += Seg::query(1, dfn[top[x]], dfn[x])) %= mod;
			x = fa[top[x]];
		}
		if(dfn[x] > dfn[y]) Swap(x, y);
		(res += Seg::query(1, dfn[x], dfn[y])) %= mod;
		return res;
	}
}
/*----------------------------------------------------------*/
int main() {
	n = read(); m = read();
	for(int i = 1; i ^ n; i++) {int x = read() + 1; add_edge(x, i + 1); add_edge(i + 1, x);}
	Cut::dfs1(1, 0); Cut::dfs2(1, 1); Seg::build(1, 1, n);
	for(int i = 0; i ^ m; i++)
	{
		int x = read() + 1, y = read() + 1, z = read() + 1;
		q[++qcnt] = (Query){x - 1, z, i, 0}; q[++qcnt] = (Query){y, z, i, 0};
	}
	std::sort(q + 1, q + 1 + qcnt, cmp1);
	for(int i = 1, j = 1; i ^ n + 1; i++)
	{
		Cut::up_date(i, 1, 1); while(!q[j].x) j++;
		while(q[j].x == i) q[j].ans = Cut::query(1, q[j].z), j++;
		if(j > qcnt) break;
	}
	std::sort(q + 1, q + 1 + qcnt, cmp2);
	for(int i = 1; i <= qcnt; i += 2) Print((q[i + 1].ans - q[i].ans + mod) % mod), pn;
	return 0;
}

P4216 情报传递

查询 \(i\) 时刻是否超过 \(c\) 相当于查询 \(i - c\) 时刻是否开始

离线下来 单点修改 路径查询 直接树剖即可

/*
  Source: P4216 [SCOI2015]情报传递
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#define pt putchar(' ')
#define pn putchar('\n')
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int B = 2e5 + 7;
/*----------------------------------------------------------*/
int n, m, s;
struct Query {int opt, x, y, t, ans1, ans2, id;} q[B];
struct edge {int v, nxt;} e[B << 1];
int head[B], ecnt;
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void add_edge(int u, int v) {e[++ecnt] = (edge){v, head[u]}; head[u] = ecnt;}
bool cmp1(Query x, Query y) {return x.t == y.t ? x.opt > y.opt : x.t < y.t;}
bool cmp2(Query x, Query y) {return x.id < y.id;}
namespace Seg {
	#define ls(x) x << 1
	#define rs(x) x << 1 | 1
	#define mid (t[p].l + t[p].r >> 1)
	struct node {int l, r, sum;} t[B << 2];
	void push_up(int p) {t[p].sum = t[ls(p)].sum + t[rs(p)].sum;}
	void build(int p, int l, int r) {
		t[p].l = l; t[p].r = r; if(l == r) return ;
		build(ls(p), l, mid); build(rs(p), mid + 1, r);
	}
	void up_date(int p, int pos) {
		if(pos == t[p].l && t[p].r == pos) {t[p].sum++; return ;}
		if(pos <= mid) up_date(ls(p), pos); else up_date(rs(p), pos); push_up(p);
	}
	int query(int p, int l, int r) {
		if(l <= t[p].l && t[p].r <= r) return t[p].sum; int res = 0;
		if(l <= mid) res += query(ls(p), l, r); if(r > mid) res += query(rs(p), l, r);
		return res;
	}
}
namespace Cut {
	struct Ans {int x, y;};
	int dep[B], fa[B], son[B], siz[B], dfn[B], top[B], kcnt;
	void dfs1(int u, int pre) {
		fa[u] = pre; dep[u] = dep[pre] + 1; siz[u] = 1;
		for(int i = head[u]; i; i = e[i].nxt)
		{
			int v = e[i].v; if(v == pre) continue;
			dfs1(v, u); siz[u] += siz[v];
			if(!son[u] || siz[v] > siz[son[u]]) son[u] = v;
		}
	}
	void dfs2(int u, int tp) {
		dfn[u] = ++kcnt; top[u] = tp;
		if(!son[u]) return ; dfs2(son[u], tp);
		for(int i = head[u]; i; i = e[i].nxt)
		{
			int v = e[i].v;
			if(v == fa[u] || v == son[u]) continue;
			dfs2(v, v);
		}
	}
	Ans query(int x, int y) {
		int res = 0, sum = 0;
		while(top[x] != top[y])
		{
			if(dep[top[x]] < dep[top[y]]) Swap(x, y);
			res += Seg::query(1, dfn[top[x]], dfn[x]);
			sum += dfn[x] - dfn[top[x]] + 1;
			x = fa[top[x]];
		}
		if(dfn[x] > dfn[y]) Swap(x, y);
		res += Seg::query(1, dfn[x], dfn[y]);
		sum += dfn[y] - dfn[x] + 1;
		Ans ans = (Ans){res, sum};
		return ans;
	}
}
/*----------------------------------------------------------*/
int main() {
	n = read();
	for(int i = 1; i ^ n + 1; i++)
	{
		int x = read();
		if(!x) s = i;
		else add_edge(i, x), add_edge(x, i);
	}
	m = read(); Cut::dfs1(s, 0); Cut::dfs2(s, 1); Seg::build(1, 1, n);
	for(int i = 1; i ^ m + 1; i++)
	{
		int opt = read();
		if(opt == 1) q[i] = (Query){opt, read(), read(), i - read(), 0, 0, i};
		else q[i] = (Query){opt, read(), 0, i + 1, 0, 0, i};
	}
	std::sort(q + 1, q + 1 + m, cmp1);
	for(int i = 1, j = 1; i ^ m + 1; i++)
	{
		while(q[j].t <= 0)
		{
			Cut::Ans tmp = Cut::query(q[j].x, q[j].y);
			q[j].ans1 = tmp.x; q[j].ans2 = tmp.y; j++;
		}
		while(q[j].t == i)
			if(q[j].opt == 1)
			{
				Cut::Ans tmp = Cut::query(q[j].x, q[j].y);
				q[j].ans1 = tmp.x; q[j].ans2 = tmp.y; j++;
			}
			else Seg::up_date(1, Cut::dfn[q[j].x]), j++;
	}
	std::sort(q + 1, q + 1 + m, cmp2);
	for(int i = 1; i ^ m + 1; i++) if(q[i].opt == 1) Print(q[i].ans2), pt, Print(q[i].ans1), pn;
	return 0;
}

P4551 最长异或路径

找最长异或路径 差分一下 一条路径 \(u \to v\) 的异或和等于路径 \(1 \to u\) 的异或和异或路径 \(1 \to v\) 的异或和

把每个点到根节点的路径的异或和预处理出来 上字典树直接算即可

/*
  Source: P4551 最长异或路径
*/
#include<cstdio>
#include<cstring>
#define pn putchar('\n')
#define Max(x, y) ((x) > (y) ? (x) : (y))
/*----------------------------------------------------------*/
const int B = 1e5 + 7;
/*----------------------------------------------------------*/
int n, a[B], t[B << 5][2], tcnt, ans;
struct edge {int v, w, nxt;} e[B << 1];
int head[B], ecnt;
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void add_edge(int u, int v, int w) {e[++ecnt] = (edge){v, w, head[u]}; head[u] = ecnt;}
void dfs(int u, int pre) {
	for(int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].v, w = e[i].w; if(v == pre) continue;
		a[v] = a[u] ^ w; dfs(v, u);
	}
}
void insert(int x) {
	int p = 0;
	for(int i = (1 << 30); i; i >>= 1)
	{
		int c = (x & i) ? 1 : 0;
		if(!t[p][c]) t[p][c] = ++tcnt;
		p = t[p][c];
	}
}
int find(int x) {
	int p = 0, res = 0;
	for(int i = (1 << 30); i; i >>= 1)
	{
		int c = (x & i) ? 1 : 0;
		if(t[p][c ^ 1]) res |= i, p = t[p][c ^ 1];
		else p = t[p][c];
	}
	return res;
}
/*----------------------------------------------------------*/
int main() {
	n = read();
	for(int i = 1; i ^ n; i++)
	{
		int x = read(), y = read(), z = read();
		add_edge(x, y, z); add_edge(y, x, z);
	}
	dfs(1, 0);
	for(int i = 1, x; i ^ n + 1; i++) x = find(a[i]), ans = Max(ans, x), insert(a[i]);
	Print(ans);
	return 0;
}

P4036 火星人

判断 LCQ 可以通过二分和字符串哈希做 字符串哈希是支持合并的 由于带修 可以通过线段树维护

但是题中还涉及插入操作 所以就写平衡树了

/*
  Source: P4036 [JSOI2008]火星人
  字符串哈希支持合并 修改 插入 好像只能写平衡树
  区间查询 单点修改 单点插入 
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#define pn putchar('\n')
#define int long long
#define Min(x, y) ((x) < (y) ? (x) : (y))
/*----------------------------------------------------------*/
const int ba = 32767;
const int B = 2e5 + 7;
const int mod = 1e9 + 7;
/*----------------------------------------------------------*/
int n, m, h[B], a[B], P[B], rt, tmp1, tmp2, tmp3;
char s[B], c[3];
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
namespace FHQ {
	#define ls(x) t[x].son[0]
	#define rs(x) t[x].son[1]
	#define mid (l + r >> 1)
	struct node {int son[2], val, sum, siz, rnd;} t[B << 1];
	int tcnt, st[B], top;
	void push_up(int p) {
		t[p].siz = t[ls(p)].siz + t[rs(p)].siz + 1;
		t[p].sum = (t[ls(p)].sum + t[p].val * P[t[ls(p)].siz] % mod + t[rs(p)].sum * P[t[ls(p)].siz + 1] % mod) % mod;
	}
	void split(int p, int k, int &x, int &y) {
		if(!p) {x = y = 0; return ;}
		if(k <= t[ls(p)].siz) y = p, split(ls(p), k, x, ls(p));
		else x = p, split(rs(p), k - t[ls(p)].siz - 1, rs(p), y); push_up(p);
	}
	int merge(int x, int y) {
		if(!x || !y) return x + y;
		if(t[x].rnd < t[y].rnd) {rs(x) = merge(rs(x), y); push_up(x); return x;}
		else {ls(y) = merge(x, ls(y)); push_up(y); return y;}
	}
	int add(int val) {
		int p = top ? st[top--] : ++tcnt; t[p].val = t[p].sum = val;
		t[p].rnd = rand(); t[p].siz = 1; ls(p) = rs(p) = 0; return p;
	}
	int build(int l, int r) {
		if(l == r) return add(a[l]);
		int x = build(l, mid), y = build(mid + 1, r);
		return merge(x, y);
	}
	int query(int l, int r) {
		split(rt, r, tmp1, tmp3); split(tmp1, l - 1, tmp1, tmp2);
		int res = t[tmp2].sum; rt = merge(merge(tmp1, tmp2), tmp3);
		return res;
	}
	void del(int pos) {split(rt, pos, tmp1, tmp3); split(tmp1, pos - 1, tmp1, tmp2); st[++top] = tmp2; rt = merge(tmp1, tmp3);}
	void ins(int pos, int k) {split(rt, pos, tmp1, tmp2); rt = merge(merge(tmp1, add(k)), tmp2);}
	#undef mid
}
bool check(int x, int y, int mid) {return FHQ::query(x, x + mid - 1) == FHQ::query(y, y + mid - 1);}
/*----------------------------------------------------------*/
signed main() {
	scanf("%s", s + 1); n = strlen(s + 1); P[0] = 1;
	for(int i = 1; i ^ B; i++) P[i] = ba * P[i - 1] % mod;
	for(int i = 1; i ^ n + 1; i++) a[i] = (s[i] - 96) * ba % mod;
	rt = FHQ::build(1, n); m = read();
	for(int i = 0; i ^ m; i++)
	{
		scanf("%s", c + 1);
		if(c[1] == 'Q')
		{
			int x = read(), y = read(), l = 1, r = Min(n - x, n - y) + 1, ans = 0;
			while(l <= r)
			{
				int mid = l + r >> 1;
				if(check(x, y, mid)) l = mid + 1, ans = mid;
				else r = mid - 1;
			}
			Print(ans); pn;
		}
		else if(c[1] == 'R')
		{
			int x = read(); scanf("%s", c + 1);
			FHQ::del(x); FHQ::ins(x - 1, (c[1] - 96) * ba);
		}
		else
		{
			int x = read(); scanf("%s", c + 1);
			FHQ::ins(x, (c[1] - 96) * ba); n++;
		}
	}
	return 0;
}

P3863 序列

把询问离线下来 对时间处理 扫序列

问题转化为区间加 区间查询不小于某个数的数的个数

对时间分块直接搞

/*
  Source: P3863 序列
  离线 对时间搞一个数据结构 扫序列 后缀加 前缀查询不小于某个数的数的个数
  分块 对时间分块处理询问 
*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define pt putchar(' ')
#define pn putchar('\n')
#define int long long
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int A = 5e2 + 7; 
const int B = 2e5 + 7;
/*----------------------------------------------------------*/
inline void File() {
	freopen("P3863_1.in", "r", stdin);
//	freopen(".out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, m, a[B], id[B], val[B], L[A], R[A], lzy[A], qcnt, ans[B], K;
struct Query {int x, k, t, opt;} q[B];
std::vector <int> c[A];
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
bool cmp(Query x, Query y) {return x.x == y.x ? x.t < y.t : x.x < y.x;}
void rev(int k, int l, int r) {c[k].clear(); for(int i = l; i ^ r + 1; i++) c[k].push_back(val[i]); std::sort(c[k].begin(), c[k].end());}
void f(int l, int r, int k) {for(int i = l; i ^ r + 1; i++) val[i] += k;}
void up_date(int l, int r, int k) {
	if(id[l] == id[r]) {f(l, r, k); rev(id[l], L[id[l]], R[id[l]]); return ;}
	f(l, R[id[l]], k); f(L[id[r]], r, k); rev(id[l], L[id[l]], R[id[l]]); rev(id[r], L[id[r]], R[id[r]]);
	for(int i = id[l] + 1; i ^ id[r]; i++) lzy[i] += k;
}
void que(int d, int l, int r, int k, int &res) {for(int i = l; i ^ r + 1; i++) if(val[i] + lzy[d] >= k) res++;}
int query(int l, int r, int k) {
	int res = 0;
	if(id[l] == id[r]) {que(id[l], l, r, k, res); return res;}
	que(id[l], l, R[id[l]], k, res); que(id[r], L[id[r]], r, k, res);
	for(int i = id[l] + 1; i ^ id[r]; i++) 
	res += c[i].size() - (std::lower_bound(c[i].begin(), c[i].end(), k - lzy[i]) - c[i].begin());
	return res;
}
/*----------------------------------------------------------*/
signed main() {
	File();
	n = read(); m = read(); K = sqrt(m + 1); 
	for(int i = 1; i ^ n + 1; i++) a[i] = read();
	for(int i = 1; i ^ m + 1; i++)
	{
		int opt = read(); ans[i] = -1;
		if(opt == 1)
		{
			int x = read(), y = read(), z = read();
			q[++qcnt] = (Query){x, z, i, opt};
			if(y < n) q[++qcnt] = (Query){y + 1, -z, i, opt};
		}
		else q[++qcnt] = (Query){read(), read(), i, opt};
		id[i] = (i - 1) / K + 1; c[id[i]].push_back(0);
	}
	std::sort(q + 1, q + 1 + qcnt, cmp); id[++m] = (m - 1) / K + 1; c[id[m]].push_back(0);
	for(int i = 1; i ^ id[m]; i++) L[i] = R[i - 1] + 1, R[i] = i * K; L[id[m]] = R[id[m] - 1] + 1; R[id[m]] = m;
	for(int i = 1, j = 0; i ^ qcnt + 1; i++)
	{
		if(j != q[i].x) up_date(1, m, a[q[i].x] - a[j]), j = q[i].x;
		if(q[i].opt == 1) up_date(q[i].t + 1, m, q[i].k);
		else ans[q[i].t] = query(1, q[i].t, q[i].k);
	}
	for(int i = 1; i ^ m; i++) if(~ans[i]) Print(ans[i]), pn;
	return 0;
}

P4396 作业

莫队消掉区间 分块统计答案

/*
  Source: P4396 [AHOI2013]作业
*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define pt putchar(' ')
#define pn putchar('\n')
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
/*----------------------------------------------------------*/
const int B = 2e5 + 7;
/*----------------------------------------------------------*/
int n, m, K, V, a[B], L[B], R[B], idk[B], idv[B], cntk[B], cntv[B], cnt[B];
struct Ans {int x, y;} ans[B];
struct Query {int l, r, a, b, id;} q[B];
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
bool cmp(Query x, Query y) {return idk[x.l] == idk[y.l] ? idk[x.l] & 1 ? x.r < y.r : x.r > y.r : x.l < y.l;}
void add(int x) {cntk[idv[a[x]]]++; if(++cnt[a[x]] == 1) cntv[idv[a[x]]]++;}
void del(int x) {cntk[idv[a[x]]]--; if(--cnt[a[x]] == 0) cntv[idv[a[x]]]--;}
void que(int l, int r, int &res, bool opt) {for(int i = l; i ^ r + 1; i++) res += opt ? cnt[i] ? 1 : 0 : cnt[i];}
Ans query(int l, int r) {
	Ans res; res.x = res.y = 0; r = Min(r, V); if(l > r) return res;
	if(idv[l] == idv[r]) {que(l, r, res.x, 0); que(l, r, res.y, 1); return res;}
	que(l, R[idv[l]], res.x, 0); que(L[idv[r]], r, res.x, 0); que(l, R[idv[l]], res.y, 1); que(L[idv[r]], r, res.y, 1);
	for(int i = idv[l] + 1; i ^ idv[r]; i++) res.x += cntk[i], res.y += cntv[i]; return res;
}
/*----------------------------------------------------------*/
int main() {
	n = read(); m = read(); K = 1.0 * n / sqrt(m) + 1; int l = 1, r = 0;
	for(int i = 1; i ^ n + 1; i++) 
	a[i] = read(), V = Max(V, a[i]), idk[i] = (i - 1) / K + 1;
	for(int i = 1; i ^ m + 1; i++) q[i] = (Query){read(), read(), read(), read(), i};
	std::sort(q + 1, q + 1 + m, cmp); K = sqrt(V) + 1;
	for(int i = 1; i ^ V + 1; i++) idv[i] = (i - 1) / K + 1;
	for(int i = 1; i ^ idv[V]; i++) L[i] = R[i - 1] + 1, R[i] = i * K; L[idv[V]] = R[idv[V] - 1] + 1; R[idv[V]] = V;
	for(int i = 1; i ^ m + 1; i++)
	{
		while(l < q[i].l) del(l++);
		while(l > q[i].l) add(--l);
		while(r < q[i].r) add(++r);
		while(r > q[i].r) del(r--);
		ans[q[i].id] = query(q[i].a, q[i].b);
	}
	for(int i = 1; i ^ m + 1; i++) Print(ans[i].x), pt, Print(ans[i].y), pn;
	return 0;
}

P3245 大数

定义 \(suf\) 表示 \(S\) 的后缀

设:

\[S_{l, r} = suf_l - suf_{r + 1} = T \times 10^k \]

如区间 \([l, r]\) 合法 则有:

\[T \times 10^k \equiv 0 \mod p \]

即:

\[suf_l \equiv suf_{r + 1} \mod p \]

问题转化为求区间中相同的点对数目 莫队维护即可

需要特判 \(p = 2\)\(p = 5\) 的情况

/*
  Source: P3245 [HNOI2016]大数
*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define pt putchar(' ')
#define pn putchar('\n')
#define int long long
/*----------------------------------------------------------*/
const int B = 4e5 + 7;
/*----------------------------------------------------------*/
int p, n, m, K, suf[B], P[B], d[B], id[B], cnt[B], ans[B], Ans, tmp1[B], tmp2[B];
struct Query {int l, r, id;} q[B];
char s[B];
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
bool cmp(Query x, Query y) {return id[x.l] == id[y.l] ? id[x.l] & 1 ? x.r < y.r : x.r > y.r : id[x.l] < id[y.l];}
void add(int x) {Ans += cnt[suf[x]]; cnt[suf[x]]++;}
void del(int x) {cnt[suf[x]]--; Ans -= cnt[suf[x]];}
void solve() {
	m = read();
	for(int i = 1; i ^ n + 1; i++) ((s[i] - 48) % p == 0) ? (tmp1[i] = tmp1[i - 1] + 1, tmp2[i] = tmp2[i - 1] + i) : (tmp1[i] = tmp1[i - 1], tmp2[i] = tmp2[i - 1]);
	for(int i = 1; i ^ m + 1; i++)
	{
		int l = read(), r = read();
		Print(tmp2[r] - tmp2[l - 1] - (l - 1) * (tmp1[r] - tmp1[l - 1])); pn;
	}
}
/*----------------------------------------------------------*/
signed main() {
	p = read(); scanf("%s", s + 1); n = strlen(s + 1); P[n] = 1; K = sqrt(n);
	if(p == 2 || p == 5) {solve(); return 0;}
	for(int i = n - 1; i; i--) P[i] = P[i + 1] * 10 % p;
	for(int i = n; i; i--) d[i] = suf[i] = (suf[i + 1] + (s[i] - 48) * P[i] % p) % p, id[i] = (i - 1) / K + 1;
	std::sort(d + 1, d + 2 + n); int ntot = std::unique(d + 1, d + 2 + n) - d - 1;
	for(int i = 1; i ^ n + 2; i++) suf[i] = std::lower_bound(d + 1, d + 2 + ntot, suf[i]) - d;
	m = read(); int l = 1, r = 0;
	for(int i = 1; i ^ m + 1; i++) q[i] = (Query){read(), read() + 1, i};
	std::sort(q + 1, q + 1 + m, cmp);
	for(int i = 1; i ^ m + 1; i++)
	{
		while(l < q[i].l) del(l++);
		while(l > q[i].l) add(--l);
		while(r < q[i].r) add(++r);
		while(r > q[i].r) del(r--);
		ans[q[i].id] = Ans;
	}
	for(int i = 1; i ^ m + 1; i++) Print(ans[i]), pn;
	return 0;
}

P3604 美好的每一天

毒瘤出的毒瘤题

状压 + 莫队

是否回文 即最多有一个字符出现奇数次 只与奇偶性有关 考虑异或

把字符压到每一位上 共占二十六位 设 \(pre\) 表示前缀异或和

则当

\[pre_{l - 1} ^ pre^r = 0 \\ pre_{l - 1} ^ pre_r = 2^k \]

卡空间有点恶心 记录数量的那个数组用 short

时是有贡献的 通过莫队维护

/*
  Source: P3604 美好的每一天
*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define RE register
#define pt putchar(' ')
#define pn putchar('\n')
/*----------------------------------------------------------*/
const int A = 6e4 + 3;
/*----------------------------------------------------------*/
inline void File() {
	freopen(".in", "r", stdin);
	freopen(".out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, m, K, pre[A], ans[A], Ans;
short cnt[1 << 26];
struct Query {int l, r, id;} q[A];
char s;
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
bool cmp(Query x, Query y) {return x.l / K == y.l / K ? x.l / K & 1 ? x.r < y.r : x.r > y.r : x.l < y.l;}
int get(char c) {return c - 96;}
void add(int x) {
	Ans += cnt[pre[x]]; cnt[pre[x]]++;
	for(RE int i = 0; i ^ 26; i++) Ans += cnt[pre[x] ^ (1 << i)];
}
void del(int x) {
	cnt[pre[x]]--; Ans -= cnt[pre[x]];
	for(RE int i = 0; i ^ 26; i++) Ans -= cnt[pre[x] ^ (1 << i)];
}
/*----------------------------------------------------------*/
int main() {
	n = read(); m = read(); int l = 1, r = 0; K = sqrt(n) * 3;
	for(RE int i = 1; i ^ n + 1; i++) {std::cin >> s; pre[i] = pre[i - 1] ^ (1 << get(s) - 1);}
	for(RE int i = 1; i ^ m + 1; i++) q[i] = (Query){read() - 1, read(), i};
	std::sort(q + 1, q + 1 + m, cmp);
	for(RE int i = 1; i ^ m + 1; i++)
	{
		while(l < q[i].l) del(l++);
		while(l > q[i].l) add(--l);
		while(r < q[i].r) add(++r);
		while(r > q[i].r) del(r--);
		ans[q[i].id] = Ans;
	}
	for(RE int i = 1; i ^ m + 1; i++) Print(ans[i]), pn;
	return 0;
}

posted @ 2021-07-15 20:07  Blank_space  阅读(60)  评论(0编辑  收藏  举报
// // // // // // //