首页 > 其他分享 >Ynoi2016镜中的昆虫

Ynoi2016镜中的昆虫

时间:2024-08-18 20:53:27浏览次数:6  
标签:Pre node 昆虫 VAL int pos Ynoi2016 color 镜中

[Ynoi 2016] 镜中的昆虫

简化题意

给定长为 \(n\) 序列 \(a\) , 两种操作 \(m\) 次:

1 l r x : 将 \([l , r]\) 修改为 \(x\)

2 l r : 查询 \([l , r]\) 出现了多少种不同的数

\(n , m \le 10 ^ 5\)

题解

\(A\) 这道题还是很不容易的,起码用了几天的时间。思路也是换了又换,从分块变成 \(CDQ\) 调了一整天才调完。

首先我们考虑不带修咋做。

显然需要维护 \(Pre\) 数组 , \(Pre_i\) 表示 \(\max\{j\} \ \ \ (1 \le j < i \ \land \ a_j = a_i)\)

其实就是和 \(a_i\) 一样的,在 \(i\) 左边,离 \(i\) 最近的位置。

那其实考虑一个简单的结论:

\[ans = \sum_{l \le i \le r}[Pre_i < l] \]

其实啥意思呢,就是说他前面的数必须在 \(l\) 左边才行如果一个数的 \(Pre\) 在 \(l\) 右边,如果记上他的话计算就会重复。

然后我们就可以把 \(n\) 个数按 \(Pre\) 排序,把询问按 \(l\) 排序那么对于一个询问来说,答案就是上述结论的式子。

但是由于我们排序了,拿一个树状数组维护前缀和,每个答案就是前面所有 \(Pre_i < l\) 且 \(i \in [l , r]\) 数的数量。

考虑使用双指针,比较简单。


考虑单点修咋做。

如果我们刚刚开始还按照上面的做法来做,那我们就是要考虑计算每一个修改对答案的贡献。

这是啥意思呢,就比如说原来是 1 , 2 , 3 , 4 , 前三个改成了 \(1\) , 那么贡献为 \(-3\) .

我们发现一次修改只会改变两个点的 \(Pre\) 值,分别是 \(i\) 和 \(j (Pre_j = i)\)

那其实来优化这个过程,我们使用 \(CDQ\) 分治。

假设我们目前待处理区间为 \((l , r)\) , 那么我们就需要计算 \((l , mid)\) 里的修改对 \((mid + 1 , r)\) 里的查询的影响。

对于修改把 \(x\) 改成 \(y\) ,

其实就是把点 \((i , x)\) 改成 \(-1\) , \((i , y)\) 改成 \(1\) , 再与刚开始的东西相加,发现正好。

这个东西叫做带修二维数点。时间复杂度为 \(O(n \log^2 n)\)

然后学到一个类似本题计算左边修改对右边查询的贡献的题时的做法。

就是分开来做, \((l_1 , r_1]\) 表示修改区间 , \((l_2 , r_2]\) 表示查询区间, \((L , R]\) 表示总数区间。

递归时传参这样:

void solve(int l1 , int r1 , int l2 , int r2 , int L , int R) {
	if (l1 == r1 || l2 == r2) return ; 
	
    int mid1 = l1 , mid2 = l2 , mid = (L + R) >> 1 ; 
	
    while (mid1 < r1 && tmp1[mid1 + 1].id <= mid) ++ mid1 ; 
	while (mid2 < r2 && tmp2[mid2 + 1].id <= mid) ++ mid2 ; 
    
    solve(l1 , mid1 , l2 , mid2 , L , mid) ; solve(mid1 , r1 , mid2 , r2 , mid , R) ; 
}

然后做完了。


考虑区间修改。

证明结论:

对于长度为 \(n\) 序列, \(m\) 次操作, \(Pre_i\) 被改动的总次数是 \(O(n + m)\) 级别的.

其实还是很好证的。考虑将连续的颜色块区间看成一个点。
因为同一个块中,除了块头, \(Pre_i = i - 1\) 恒成立.

对于 \(m\) 次操作,那就会产生 \(m\) 个新点。在块左块右产生了两个新块被合并,自然就有两个新块新产生。

中间的点全部删去。

所以最多每次产生 \(3m\) 个点,每个点必最多被删除一次,所以改动数量为 \(O(n + m)\) 级别。

那也就是说对于本次没改的点,在本区间内,显然早就被改过,且值一直不变。

那就意味着 \(-1\) 或 \(1\) 的这个位置的影响早就被扔进来了,不用管了。

对于这种情况显然直接上了。

\(Code\)

CODE
#include <bits/stdc++.h>
#ifdef linux
#define getchar getchar_unlocked
#define putchar putchar_unlocked
#endif
using namespace std ; 
typedef long long ll ; 
const int N = 3e5 + 100 ; 
namespace Fast_IO {
	inline int read() {
		int x = 0 , f = 1 ; 
		char c = getchar() ; 

		while (c < '0' || c > '9') {
			c = getchar() ; 
		}

		while (c >= '0' && c <= '9') {
			x = x * 10 + c - '0' ; 
			c = getchar() ; 
		}

		return x * f ; 
	}

	void write(int x) {
		if (x / 10) write(x / 10) ; 
		putchar(x % 10 + '0') ; 
	}
} using namespace Fast_IO ; 

struct Asker {
	int opt , l , r , x ; 
} b[N] ; 
struct Node1 {
	int id , pos , Pre , val ; 
} tmp1[N << 2] ; 
struct Node2 {
	int ider , id , l , r ; 
} tmp2[N] ; 

#define iter2 set <node> :: iterator 
#define iter1 set <Node> :: iterator

int n , m , a[N] , maxn , pi[N << 2] , cnt ; 
int Pre[N] , front[N] ; 

class Binary_Index_Array {
	#define lowbit(x) (x & (- x))
	private:
		int t[N] ; 
	public:
		void add(int pos , int val) {
			while (pos <= n) {
				t[pos] += val ; 
				pos += lowbit(pos) ; 
			}
		}

		int Query(int pos) {
			int ans = 0 ; 

			while (pos > 0) {
				ans += t[pos] ; 
				pos -= lowbit(pos) ; 
			}

			return ans ; 
		}

		void clear() {
			memset(t , 0 , sizeof(t)) ; 
		}

		void Deleted(int pos) {
			while (pos <= n) {
				t[pos] = 0 ; 
				pos += lowbit(pos) ; 
			}
		}
	#undef lowbit
} tree ; 

struct Node {
	int l , r ; 
	mutable int color ; 
	Node(int L , int R = 0 , int COLOR = 0) : l(L) , r(R) , color(COLOR) {}

	bool operator < (const Node a) const {
		return l < a.l ; 
	} ; 
} ; set <Node> s ; 
struct node {
	mutable int l , r ; 
	node(int L , int R = 0) : l(L) , r(R) {}

	bool operator < (const node a) const {
		return l < a.l ; 
	} ; 
} ; set <node> t[N] ; 

iter1 Split(int pos) {
	iter1 it = s.lower_bound(Node(pos)) ; 

	if (it != s.end() && it->l == pos) return it ; 

	it -- ; 
	int L = it->l , R = it->r , VAL = it->color ; 
	s.erase(it) ; 
	t[VAL].erase(node(t[VAL].lower_bound(node(L , R))->l)) ; 
	t[VAL].insert(node(L , pos - 1)) ; 
	s.insert(Node(L , pos - 1 , VAL)) ; 

	if (R < pos) return s.end() ; 
	t[VAL].insert(node(pos , R)) ; 
	return s.insert(Node(pos , R , VAL)).first ; 
}

int tot1 , tot2 , total ; 

void Modify(int pos , int val) {
	tmp1[++ tot1] = {++ total , pos , Pre[pos] , -1} ; 
	tmp1[++ tot1] = {++ total , pos , Pre[pos] = val , 1} ; 
}

void Assign(int l , int r , int color , int id) {
	iter1 it1 = Split(r + 1) ; iter1 it2 = Split(l) ; 
	int Fir = it2->l ; 

	for (iter1 j = it2 ; j != it1 ; ++ j) {
		iter2 Next = t[j->color].upper_bound(node(j->l)) ; 

		if (Next != t[j->color].end()) {
			Modify(Next->l , Pre[j->l]) ; 
		}
		
		Modify(j->l , j->l - 1) ; 
		int VAL = j->color , L = j->l , R = j->r ; 
		t[VAL].erase(t[VAL].lower_bound(node(L , R))) ; 
	}

	iter2 Next = t[color].upper_bound(node(l)) ; 
	if (Next != t[color].end()) {
		Modify(Next->l , r) ; 
	}

	auto j = t[color].lower_bound(node(l)) ; 
	if (j == t[color].begin()) {
		Modify(Fir , 0) ; 
	} else {
		-- j ; 
		Modify(Fir , j->r) ; 
	}

	s.erase(it2 , it1) ; 
	s.insert(Node(l , r , color)) ; 
	t[color].insert(node(l , r)) ; 
}

int fir[N] , sec[N] ; 
int Ans[N] ; pair <int , int> nPre[N] ; 

struct PAIRAIR {
	int l , r , id ; 
} c[N] ; int num ; 

void solve(int l1 , int r1 , int l2 , int r2 , int L , int R) {
	if (l1 == r1 || l2 == r2) return ; 

	int mid1 = l1 , mid2 = l2 , mid = (L + R) >> 1 ; 
	while (mid1 < r1 && tmp1[mid1 + 1].id <= mid) ++ mid1 ; 
	while (mid2 < r2 && tmp2[mid2 + 1].id <= mid) ++ mid2 ; 

	solve(l1 , mid1 , l2 , mid2 , L , mid) ; solve(mid1 , r1 , mid2 , r2 , mid , R) ; 
	stable_sort(tmp1 + l1 + 1 , tmp1 + mid1 + 1 , [](Node1 a , Node1 b) {
		return a.Pre < b.Pre ; 
	}) ; 
	stable_sort(tmp2 + mid2 + 1 , tmp2 + r2 + 1 , [](Node2 a , Node2 b) {
		return a.l < b.l ; 
	}) ; 

	int top = l1 + 1 ; 

	for (int i = mid2 + 1 ; i <= r2 ; ++ i) {
		while (top <= mid1 && tmp1[top].Pre < tmp2[i].l) {
			tree.add(tmp1[top].pos , tmp1[top].val) ; ++ top ; 
		}

		Ans[tmp2[i].ider] += tree.Query(tmp2[i].r) - tree.Query(tmp2[i].l - 1) ; 
	}

	for (int i = l1 + 1 ; i <= mid1 ; ++ i) {
		tree.Deleted(tmp1[i].pos) ; 
	}
}

signed main() {
	#ifdef LOCAL
		freopen("1.in" , "r" , stdin) ; 
		freopen("1.out" , "w" , stdout) ; 
	#endif
	n = read() , m = read() ; 
	for (int i = 1 ; i <= n ; ++ i) {
		a[i] = read() , pi[++ cnt] = a[i] ; 
	}
	for (int i = 1 ; i <= m ; ++ i) {
		b[i].opt = read() , b[i].l = read() , b[i].r = read() ; 

		if (b[i].opt == 1) b[i].x = read() , pi[++ cnt] = b[i].x ; 
		else {
			++ num ; c[num].id = i , c[num].l = b[i].l , c[num].r = b[i].r ; 
		}
	}

	stable_sort(pi + 1 , pi + cnt + 1) ; cnt = unique(pi + 1 , pi + cnt + 1) - pi - 1 ; 
	maxn = cnt ; 

	for (int i = 1 ; i <= n ; ++ i) {
		a[i] = lower_bound(pi + 1 , pi + cnt + 1 , a[i]) - pi ; 
		Pre[i] = front[a[i]] ; front[a[i]] = i ; nPre[i] = make_pair(Pre[i] , i) ; 
		t[a[i]].insert(node(i , i)) ; 
		s.insert(Node(i , i , a[i])) ; 
	} 

	stable_sort(nPre + 1 , nPre + n + 1 , [](pair <int , int> a , pair <int , int> b) {
		return a.first < b.first ; 
	}) ; 
	stable_sort(c + 1 , c + num + 1 , [](PAIRAIR a , PAIRAIR b) {
		return a.l < b.l ; 
	}) ; 
	int head = 1 ; 

	for (int i = 1 ; i <= num ; ++ i) {
		while (head <= n && nPre[head].first < c[i].l) {
			tree.add(nPre[head].second , 1) , ++ head ; 
		}

		Ans[c[i].id] += tree.Query(c[i].r) - tree.Query(c[i].l - 1) ; 
	} tree.clear() ; 

	for (int i = 1 ; i <= m ; ++ i) {
		if (b[i].opt == 1) {
			b[i].x = lower_bound(pi + 1 , pi + cnt + 1 , b[i].x) - pi ; 
			Assign(b[i].l , b[i].r , b[i].x , i) ; 
		} else {
			++ tot2 ; tmp2[tot2].ider = i , tmp2[tot2].l = b[i].l , tmp2[tot2].r = b[i].r ; 
			tmp2[tot2].id = ++ total ; 
		}
	}

	solve(0 , tot1 , 0 , tot2 , 0 , total) ; 

	for (int i = 1 ; i <= m ; ++ i) {
		if (b[i].opt == 2) {
			write(Ans[i]) ; puts("") ; 
		}
	}
}

标签:Pre,node,昆虫,VAL,int,pos,Ynoi2016,color,镜中
From: https://www.cnblogs.com/hangry/p/18366075

相关文章

  • P4689 [Ynoi2016] 这是我自己的发明 与 P5268 [SNOI2017] 一个简单的询问0
    思路:首先可以先考虑没有换根的情况。先将树拍到dfn序上,那么一个子树\(u\)的所有点的dfn序区间为\([dfn_u,dfn_u+siz_u-1]\)。那么询问变为:每次给定两个区间\([l_1,r_1],[l_2,r_2]\),对于在第一个区间内的点\(x\)和在第二个区间的点\(y\),若\((x,y)\)有贡献,当且仅......
  • [Ynoi2016] 镜中的昆虫 题解
    难度在最近遇到的题里相对较高,在这里写一篇珂学题解。(以下是学校给的部分分)\(20\%\):直接暴力枚举。另外\(20\%\):假如我们取\(pre\),对于\(pre<l\)的,\(ans++\),明显二维偏序,树状数组或\(cdq\)即可,时间复杂度\(O(n\logn)\)。另外\(40\%\):相当于多加一个时间维,三维偏序,\(......
  • P4690 Ynoi2016 镜中的昆虫
    P4690Ynoi2016镜中的昆虫原题不会见祖宗。前置珂朵莉树、cdq分治、树状数组思路单点修改区间查询定义\(pre_i\)表示\(col_i\)的前一个一样颜色的位置,那么对于一段区间查询\([l,r]\),我们只需要查询有区间内有多少个\(pre_i<l\)。每次修改时就相当于修改四个同颜色......
  • 昆虫繁殖 - 题解
    昆虫繁殖时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++256MB,其他语言512MB描述科学家在热带森林中发现了一种特殊的昆虫,这种昆虫的繁殖能力很强。每对成虫过\(x\)个月产\(y\)对卵,每对卵要过两个月长成成虫。假设每个成虫不死,第一个月只有一对成虫,且卵长成成虫后......
  • P4688 Ynoi2016 掉进兔子洞
    P4688Ynoi2016掉进兔子洞经典莫队加bitset。思路不难发现最终答案就是:\[(r_1-l_1+1)+(r_2-l_2+1)+(r_3-l_3+1)-3\timessize\]其中\(size\)表示3个区间内出现了多少个公共元素。看到这么多区间,不妨有把区间拆下来搞莫队的想法。先不考虑询问个数的限制,我们考虑使用......
  • 【昆虫识别系统】图像识别Python+卷积神经网络算法+人工智能+深度学习+机器学习+Tenso
    一、介绍昆虫识别系统,使用Python作为主要开发语言。通过TensorFlow搭建ResNet50卷积神经网络算法(CNN)模型。通过对10种常见的昆虫图片数据集('蜜蜂','甲虫','蝴蝶','蝉','蜻蜓','蚱蜢','蛾','蝎子','蜗牛','蜘蛛')进行训练,得到一个识别精度较......
  • 基于yolov2深度学习网络的昆虫检测算法matlab仿真,并输出昆虫数量和大小判决
    1.算法运行效果图预览     2.算法运行软件版本matlab2022A 3.部分核心程序fori=1:12%遍历结构体就可以一一处理图片了ifigureimg=imread([imgPath[num2str(i),'.jpeg']]);%读取每张图片I=imre......
  • 20种昆虫图像分类数据集
    20种昆虫图像分类数据集数据集:链接:https://pan.baidu.com/s/1M_syZSjpc_08A3Ip5dKzBA?pwd=yhzw提取码:yhzw数据集信息介绍:文件夹天牛中的图片数量:516文件夹棉铃虫中的图片数量:250文件夹独角仙中的图片数量:480文件夹瓢虫中的图片数量:470文件夹......
  • C117 莫队配合 bitset P4688 [Ynoi2016] 掉进兔子洞
    视频链接:C117莫队配合bitsetP4688[Ynoi2016]掉进兔子洞_哔哩哔哩_bilibili   LuoguP4688[Ynoi2016]掉进兔子洞//莫队配合bitsetO(n*sqrt(n))#include<iostream>#include<cstring>#include<algorithm>#include<cmath>#include<bitset>usin......
  • P4690 [Ynoi2016] 镜中的昆虫
    P4690[Ynoi2016]镜中的昆虫本质就是区间修改,区间数颜色弱化一下,先考虑不带修的情况,也就是P1972[SDOI2009]HH的项链其难点在于区间颜色的去重,需要想一个办法让区间内一个颜色只被数一次我们可以去维护一个\(Nxt\)数组,表示下一个与当前位置颜色相同的位置若当前位......