首页 > 其他分享 >[Ynoi2010] Brodal queue 做题记录

[Ynoi2010] Brodal queue 做题记录

时间:2024-12-13 19:53:13浏览次数:4  
标签:const Ynoi2010 bl queue Brodal int define 散块 mod

link

加深了对分块算法的理解。

题目相当于求解一个区间内每种颜色出现次数平方和,这种题显然无法 polylog。

先尝试分块,将贡献拆成 散块 - 散块;散块 - 整块;整块 - 整块 三种。

散块 - 散块 是容易的,直接用桶计数就好。

整块 - 整块:设 \(d_{c, i}\) 表示颜色 \(c\) 在前 \(i\) 个块的出现次数,考虑维护 \(f(i, j) = \sum\limits_c d_{c, i} d_{c, j}\),拆掉平方,那么第 \(l\sim r\) 个块的答案为 \(f(r, r) + f(l - 1, l - 1) - 2\cdot f(l - 1, r)\)。

当第 \(k\) 块中颜色 \(c\) 的数量增加 \(w\) 时,则 \(d_{c, i}, d_{c, i + 1}, \dots\) 都会增加 \(w\),我们观察 \(f\) 的变化:

  • \(i\le j< k\):不变

  • \(i < k\le j\):\(d_{c, i} (d_{c, j} + w) = d_{c, i} d_{c, j} + w\cdot d_{c, i}\),增加了 \(w\cdot d_{c, i}\)。

  • \(k\le i, j\):\((d_{c, i} + w) (d_{c, j} + w) = d_{c, i} d_{c, j} + w\cdot d_{c, i} + w\cdot d_{c, j} + w^2\),增加了 \(w\cdot d_{c, i} + w\cdot d_{c, j} + w^2\)。

增加的贡献只和 \(i, j\) 其中一维有关,可以两个维度分别差分,做到单次修改或查询 \(\mathcal O(\sqrt n)\)。

散块 - 整块:也是容易的,枚举散块元素时,可以快速算出整块部分某一颜色的出现次数。

分析一下时间复杂度,观察到在“第 \(k\) 块颜色 \(c\) 数量增加 \(w\)”这一修改次数是 \(\mathcal O(m\sqrt n)\) 的,时间复杂度不对。

发现中间耗时间的主要是颜色全部相同的“纯色块”,考虑如果把纯色块拎出来扔进散块的部分,时间如何。

观察非纯色块颜色连续段数,每次区间覆盖操作只会在两边的散块中增加一个连续段,所以这玩意总数为 \(\mathcal O(m)\),均摊下来时间正确,为 \(\mathcal O((n + m)\sqrt n)\)。

  • 启示:分块过程中的均摊时间分析;维护整块之间的信息
点击查看代码
#include <bits/stdc++.h>

namespace Initial {
	#define ll long long
	#define ull unsigned long long
	#define fi first
	#define se second
	#define mkp make_pair
	#define pir pair <int, int>
	#define pb emplace_back
	#define i128 __int128
	using namespace std;
	const int maxn = 2e5 + 10, mod = 998244353;
	const ll inf = 1e18;
	int power(int a, int b = mod - 2) {
		int s = 1;
		while(b) {
			if(b & 1) s = 1ll * s * a %mod;
			a = 1ll * a * a %mod, b >>= 1;
		} return s;
	}
	template <class T>
	const inline ll pls(const T x, const T y) { return x + y >= mod? x + y - mod : x + y; }
	template <class T>
	const inline void add(T &x, const T y) { x = x + y >= mod? x + y - mod : x + y; }
	template <class T>
	const inline void chkmax(T &x, const T y) { x = x < y? y : x; }
	template <class T>
	const inline void chkmin(T &x, const T y) { x = x > y? y : x; }
} using namespace Initial;

namespace Read {
	char buf[1 << 22], *p1, *p2;
	#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, (1 << 22) - 10, stdin), p1 == p2)? EOF : *p1++)
	template <class T>
	const inline void rd(T &x) {
		char ch; bool neg = 0;
		while(!isdigit(ch = getchar()))
			if(ch == '-') neg = 1;
		x = ch - '0';
		while(isdigit(ch = getchar()))
			x = (x << 1) + (x << 3) + ch - '0';
		if(neg) x = -x;
	}
} using Read::rd;

int n, m, B, bn, a[maxn], d[maxn][450], bl[maxn], cnt[maxn];
ll fx[450][450], fy[450][450], iscol[450], ans;
unsigned int lstans;

ll get_f(const int x, const int y) {
	ll ans = 0;
	for(int i = 1; i <= x; i++) ans += fy[i][y];
	for(int i = x; i <= y; i++) ans += fx[i][x];
	return ans;
}
void upd(const int u, const int c, const int w) {
	for(int i = 1; i < u; i++) fx[u][i] += w * d[c][i];
	for(int i = u; i <= bn; i++)
		fx[i][i] += w * (w + d[c][i]), fy[u][i] += w * d[c][i];
	for(int i = u; i <= bn; i++) d[c][i] += w;
}

void del(const int u, const int l, const int r) {
	for(int i = l, j = l; i <= r; i++) {
		if(a[j] ^ a[i]) upd(u, a[j], j - i), j = i;
		if(i == r) upd(u, a[i], j - i - 1);
	}
}
void modify(const int u, const int l, const int r, const int c) {
	const int ul = (u - 1) * B + 1, ur = min(n, u * B);
	if(l <= ul && ur <= r) {
		if(!iscol[u]) del(u, ul, ur);
		iscol[u] = c;
	} else {
		const int L = max(l, ul), R = min(r, ur);
		if(iscol[u]) {
			for(int i = ul; i <= ur; i++) a[i] = iscol[u];
			upd(u, iscol[u], ur - ul - R + L), iscol[u] = 0;
		} else del(u, L, R);
		upd(u, c, R - L + 1);
		for(int i = L; i <= R; i++) a[i] = c;
	}
}

signed main() {
//	freopen("p.in", "r", stdin);
//	freopen("p.out", "w", stdout);
	rd(n), rd(m); B = sqrt(n), bn = (n - 1) / B + 1;
	for(int i = 1; i <= n; i++) rd(a[i]), bl[i] = (i - 1) / B + 1;
	for(int i = 1; i <= n; i++) upd(bl[i], a[i], 1);
	while(m--) {
		int op, l, r; rd(op), rd(l), rd(r);
		l ^= lstans, r ^= lstans;
		if(op == 1) {
			int c; rd(c), c ^= lstans;
//			printf("modify %d %d %d\n", l, r, c);
			for(int j = bl[l]; j <= bl[r]; j++) modify(j, l, r, c);
		} else {
//			printf("query %d %d\n", l, r);
			if(bl[l] == bl[r]) {
				ans = 0;
				if(iscol[bl[l]]) {
					ans = (r - l + 1) * (r - l + 1);
				} else {
					for(int i = l; i <= r; i++)
						ans += 2 * cnt[a[i]] + 1, ++cnt[a[i]];
					for(int i = l; i <= r; i++) cnt[a[i]] = 0;
				}
				lstans = ans = ans - (r - l + 1) >> 1;
				printf("%lld\n", ans); continue;
			}
			ans = get_f(bl[r] - 1, bl[r] - 1) + get_f(bl[l], bl[l])
			 - 2 * get_f(bl[l], bl[r] - 1);
			const int bl_l = bl[l], bl_r = bl[r];
			const int pl = bl_l * B + 1, pr = (bl_r - 1) * B;
			if(iscol[bl_l]) {
				const int w = pl - l, x = iscol[bl_l];
				ans += 1ll * (2 * (d[x][bl_r - 1] - d[x][bl_l] + cnt[x]) + w) * w;
				cnt[x] += w;
			} else
				for(int i = l; i < pl; i++) {
					const int x = a[i];
					ans += 2 * (d[x][bl_r - 1] - d[x][bl_l] + cnt[x]) + 1;
					++cnt[x];
				}
			if(iscol[bl_r]) {
				const int w = r - pr, x = iscol[bl_r];
				ans += 1ll * (2 * (d[x][bl_r - 1] - d[x][bl_l] + cnt[x]) + w) * w;
				cnt[x] += w;
			} else
				for(int i = pr + 1; i <= r; i++) {
					const int x = a[i];
					ans += 2 * (d[x][bl_r - 1] - d[x][bl_l] + cnt[x]) + 1;
					++cnt[x];
				}
			for(int i = bl_l + 1; i < bl_r; i++)
				if(iscol[i]) {
					const int x = iscol[i];
					ans += 1ll * (2 * (d[x][bl_r - 1] - d[x][bl_l] + cnt[x]) + B) * B;
					cnt[x] += B;
				}
			cnt[iscol[bl_l]] = cnt[iscol[bl_r]] = 0;
			if(!iscol[bl_l])
				for(int i = l; i < pl; i++) cnt[a[i]] = 0;
			if(!iscol[bl_r])
				for(int i = pr + 1; i <= r; i++) cnt[a[i]] = 0;
			for(int i = bl_l + 1; i < bl_r; i++) cnt[iscol[i]] = 0;
			lstans = ans = ans - (r - l + 1) >> 1;
			printf("%lld\n", ans);
		}
	}
	return 0;
}

标签:const,Ynoi2010,bl,queue,Brodal,int,define,散块,mod
From: https://www.cnblogs.com/Sktn0089/p/18605754

相关文章

  • 23. C++STL 9 (priority_queue的使用和适配实现详解)
    ⭐本篇重点:1priority_queue的使用与底层原理2使用容器来适配priority_queue⭐本篇代码:c++学习·橘子真甜/c++-learning-of-yzc-码云-开源中国(gitee.com)⭐标⭐是比较重要的部分目录一.priority_queue(优先级队列)的使用与原理1.1priority_queue的底层原理......
  • Queue 相关知识
    1.Queue与Deque的区别Queue是单端队列,只能从一端插入元素,另一端删除元素,实现上一般遵循先进先出(FIFO)规则。Deque是双端队列,在队列的两端均可以插入或删除元素。2.ArrayDeque和LinkedList2.1ArrayDequeArrayDeque是基于动态循环数组和双指针来实现ArrayDeque插入......
  • 调度器69—ENQUEUE/DEQUEUE flags
    基于msm-4.14一、简介1.在enqueue_task/dequeue_task向就绪队列插入和移除任务的时候,通过flags参数判断是由于什么原因触发的enqueue和dequeue,并进行不同的响应。2.相关函数://kernel/sched/core.cstaticinlinevoidenqueue_task(structrq*rq,structtask_stru......
  • 题海拾贝——救济金发放(The Dole Queue, UVa 133)
            Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路!我的博客:<但凡.我的专栏:《题海拾贝》、《编程之路》欢迎点赞,关注!目录1、题目2、分析3、题解(附详细解析)4、拓展 1、题目        n(n个人站成一圈,逆时针编号为1~n。......
  • 题解:AT_arc139_d [ARC139D] Priority Queue 2
    题面发现我们不好算到最后还剩些什么。考虑计算\(\sum\limits_{i=1}^m\sum\limits_{j=1}^n[s_j\gei]\),容易发现这和原式等价。记\(b_i\)表示\(s\)中不小于\(i\)的数的个数,每次删去第\(x\)小的等价于将所有超过\(n-x+1\)的地方减1,加入\(k\)等价于将\(b_{1,k}\)......
  • 多线程篇-7--线程通信(等待/通知机制,等待/超时机制,CountdownLatch,CyclicBarrier,Blockin
    1、线程为什么要通信?多线程的目的是多条线程执行不同的逻辑业务从而能够提升业务整体的响应速度,如果线程都是孤零零的执行,不同的逻辑业务就不能最终汇聚成一个完整的业务,那么多线程也就失去了意义,这就是为什么要有线程间通信的存在。线程间的通信可以是主、子线程通信,也可......
  • 了解AQS(AbstractQueuedSynchronizer)
    AQS(AbstractQueuedSynchronizer)是Java并发包中的一个核心同步器框架,它定义了一套多线程访问共享资源的同步机制。一、AQS的定义AQS,全称AbstractQueuedSynchronizer,即抽象队列同步器,是Java中的一个抽象类。它是构建锁或者其他同步组件的基础框架,通过继承AQS,子类可以实现自己的......
  • JDK17 AbstractQueuedSynchronizer 二 条件队列
    条件队列同步队列中的线程是为了争抢锁,而条件队列中的线程是主动释放锁,挂起自己,等条件满足时被别的线程唤醒,继续工作。AQS里只有1个同步队列,但可以有多个等待队列,每个等待队列对应一个ConditionObject对象。publicstaticvoidmain(String[]args){ ReentrantLocklo......
  • JDK17 AbstractQueuedSynchronizer 一 同步队列
    AQS抽象队列同步器是JDK提供的用于管理线程间同步状态的类。常见的同步器类ReentrantLock,CountDownLatch,Semaphore等都是AbstractQueuedSynchronizer的子类。AQS提供三个功能提供同步状态。一个是state属性,管理资源的状态。一个是AQS的父抽象类的exclusiveOwnerThread......
  • 数据结构优先级队列PriorityQueue
    本章讲述数据结构中的优先级队列的学习,感谢大家的支持!欢迎大家踊跃评论,感谢大佬们的支持!我的博客主页数据结构中的优先级队列优先级队列的概念如何实现优先级队列?PriorityQueue优先队列实现大根堆实现小根堆实现插入元素删除元素堆排序PriorityQueue包注意事项......