首页 > 其他分享 >ICPC2024 杭州区域赛 M. Make It Divisible 解题报告

ICPC2024 杭州区域赛 M. Make It Divisible 解题报告

时间:2024-11-26 20:45:53浏览次数:8  
标签:ch gcd ICPC2024 Divisible Make int le 区间 define

ICPC2024 杭州区域赛 M. Make It Divisible 解题报告

题目大意

给你一个长度为 \(n \le 5 \times 10 ^ 4\) 的数列 \(\{a_n\}\) 和一个数 \(m \le 10 ^ 9\),求出所有满足条件的 \(x\) ,使得对于数列 \(\{a_n + x\}\) 的任意一个区间,满足区间内存在一个数,能整除这个区间内所有的数。输出满足条件的 \(x\) 的个数以及它们的和。

题解

分析

考虑对于一个区间 \([l, r]\),满足条件的 \(x\) 一定满足:

\[gcd(a_l + x, a_{l + 1} + x, ..., a_{r} + x) = min_{l \le i \le r}\{a_i + x\} \]

考虑对于 \(x\) 的普适条件,我们想到可以对 \(gcd\) 进行差分。得到:

\[gcd(a_l + x, a_{l + 1} - a_l, ..., a_{r} - a_{r - 1}) = min_{l \le i \le r}\{a_i + x\} \]

令 \(g = gcd(a_{l + 1} - a_l, ..., a_{r} - a_{r - 1})\),我们可以发现满足条件的 \(mn = min_{l \le i \le r}\{a_i + x\}\) 一定满足 \(mn | g\)。

于是对于一个区间,我们求出他除去第一个元素后的差分数组的区间 \(gcd\),并弄出他的所有因子。把所有满足 \(mn|a_l\) 的 \(x\) 选出来即可得到这个区间的备选方案。

然后我们考虑对于选取的最小值的点相同的区间,这个区间越大,条件越紧。于是可以自然地想到用笛卡尔树维护这个序列。

然后每次我们按照上面的方法确定了这个区间的备选方案,需要做的就是对所有区间求一个 \(x\) 的交集。我们可以在笛卡尔树上一层一层合并上去即可。集合求交可以考虑用哈希表或 \(set\) 维护。

具体细节

对于区间求 \(gcd\),我们采用线段树可以将这部分在 \(O(n\log n)\) 的复杂度内完成。

而对于集合求交,我们这边建议选择哈希表。可以在 \(O(|S|)\) 的复杂度内完成集合求交。至于 \(set\),由于多带了一个 \(\log\),可能需要更好的常数才可通过,不建议使用。作为复杂度瓶颈,这部分在 \(O(nd)\) 的复杂度内完成,其中 \(d\) 表示约数个数,易知 \(d\le \sqrt n\)。

代码实现

#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define db double
#define file(a) freopen(#a".in", "r", stdin), freopen(#a".out", "w", stdout)
#define sky fflush(stdout)
#define pc putchar
namespace IO{
	const int lim = 1 << 20;
	char buf[lim + 3], *p1 = buf, *p2 = buf;
	inline char gc(){
		if(p1 == p2) p2 = (p1 = buf) + fread(buf,1,lim,stdin);
		return p1 == p2 ? EOF : *p1++;
	}
	template<class T>
	inline void read(T &s){
		s = 0;char ch = gc();bool f = 0;
		while(ch < '0' || '9'<ch) {if(ch == '-') f = 1; ch = gc();}
		while('0'<=ch && ch<='9') {s = s * 10 + (ch ^ 48); ch = gc();}
		if(ch == '.'){
			T p = 0.1;ch = gc();
			while('0' <= ch && ch <= '9') {s = s + p * (ch ^ 48);p /= 10;ch = gc();}
		}
		s = f ? -s : s;
	}
	template<class T,class ...A>
	inline void read(T &s,A &...a){
		read(s); read(a...);
	}
	template<class T>
	inline void print(T x){
		if(x<0) {x = -x; pc('-');}
		static char st[40];
		static int top;
		top = 0;
		do{st[++top] = x - x / 10 * 10 + '0';} while(x /= 10);
		while(top) {pc(st[top--]);}
	}
	template<class T,class ...A>
	inline void print(T s,A ...a){
		print(s); print(a...);
	}
};
using IO::read;
using IO::print;
const int N = 5e4;
int n, m;
int a[N + 3];
int gcd(int a, int b){
	if(!b) return a;
	return gcd(b, a % b);
}
struct SegTree{
	struct node{
		int Gcd;
	}t[N * 4 + 3];
	#define lc(x) (x << 1)
	#define rc(x) (x << 1 | 1)
	void pushup(int x){
		t[x].Gcd = gcd(t[lc(x)].Gcd, t[rc(x)].Gcd);
	}
	void build(int x, int l, int r){
		if(l == r){
			t[x].Gcd = a[l] - a[l - 1];
			return;
		}
		int mid = l + r >> 1;
		build(lc(x), l, mid);
		build(rc(x), mid + 1, r);
		pushup(x);
	}
	int query(int x, int l, int r, int L, int R){
		if(L > R) return 0;
		if(L <= l && r <= R){
			return t[x].Gcd;
		}
		int mid = l + r >> 1;
		int res = 0;
		if(L <= mid) res = gcd(res, query(lc(x), l, mid, L, R) );
		if(mid + 1 <= R) res = gcd(res, query(rc(x), mid + 1, r, L, R) );
		return res;
	}
}t;
const int S = 547;
struct HashMap{	
	int head[S + 3];
	int nxt[2000 + 3], to[2000 + 3];
	int tot;
	inline void clear(){
		tot = -1;
		memset(head, -1, sizeof(head) );
	}
	int find(int x){
		int u = x % S;
		for(int i = head[u]; ~i; i = nxt[i]){
			if(to[i] == x){
				return true;
			}
		}
		return false;
	}
	void insert(int x){
		int u = x % S;
		nxt[++tot] = head[u];
		head[u] = tot;
		to[tot] = x;
	}
}s[N + 3];
int ch[N + 3][2];
int sta[N + 3], top;
int rt;
int l[N + 3], r[N + 3];
std::vector<std::pair<int, int> >tmp1;
std::vector<int>tmp2;
bool any[N + 3];
void calc(int x, int t, int sum){
	if(t == tmp1.size() ){
		tmp2.push_back(sum);
		return;
	}
	for(int i = 0; i <= tmp1[t].second; ++i){
		calc(x, t + 1, sum);
		sum *= tmp1[t].first;
	}
}
void dfs(int x){
	l[x] = r[x] = x;
	if(!ch[x][0] && !ch[x][1]){
		any[x] = 1;
		return;
	}
	for(int i = 0; i < 2; ++i){
		if(ch[x][i]){
			dfs(ch[x][i]);
			l[x] = std::min(l[x], l[ch[x][i] ]);
			r[x] = std::max(r[x], r[ch[x][i] ]);
		}
	}
	
	int g = t.query(1, 1, n, l[x] + 1, r[x]);
	if(g == 0){
		any[x] = any[ch[x][0] ] & any[ch[x][1] ];
		if(ch[x][0] && ch[x][1]){
			int y1 = ch[x][0], y2 = ch[x][1];
			for(auto it : s[y1].to){
				if(s[y2].find(it) ){
					s[x].insert(it);
				}
			}
		}else{
			int y = ch[x][0] | ch[x][1];
			s[x] = s[y];
		}
		return;
	}
	int k = abs(g);
	tmp1.clear();
	tmp2.clear();
	for(int i = 2; i * i <= k; ++i){
		if(k % i == 0){
			int cnt = 0;
			while(k % i == 0) k /= i, ++cnt;
			tmp1.push_back({i, cnt});
		}
	}
	if(k != 1) tmp1.push_back({k, 1});
	//fprintf(stderr, "%d -> %d %d [%d, %d] %d\n", x, ch[x][0], ch[x][1], l[x], r[x], g);
	calc(x, 0, 1);
	for(auto it : tmp2){
		if(it - a[x] < 1 || it - a[x] > m) continue;
		//fprintf(stderr, "%d(%d) ", it - a[x], it);
		if((a[l[x] ] + it - a[x]) % it == 0){
			bool flag = 1;
			if(!any[ch[x][0] ])
			if(!s[ch[x][0] ].find(it - a[x] ) ) 
				flag = 0;
			if(!any[ch[x][1] ])
			if(!s[ch[x][1] ].find(it - a[x] ) ) 
				flag = 0;
			if(flag)
				s[x].insert(it - a[x] );
			//fprintf(stderr, "<-(%d) ", flag);
		}
	}
	/*
	fprintf(stderr,"\n%d\n", sz[x]);
	for(auto it : s[x].to){
		if(it.v) fprintf(stderr, "%d ", it.x);
	}
	fprintf(stderr,"\n");
	*/
}
inline void solve(){
	read(n, m);
	any[0] = 1;
	for(int i = 1; i <= n; ++i){
		read(a[i]);
		s[i].clear();
		ch[i][0] = ch[i][1] = 0;
		any[i] = 0;
	}
	t.build(1, 1, n);
	top = 0;
	sta[++top] = 1;
	for(int i = 2; i <= n; ++i){
		int k = top;
		while(k && a[sta[k] ] > a[i]) 
			--k;
		if(k < top) ch[i][0] = sta[k + 1];
		if(k) ch[sta[k] ][1] = i;
		sta[++k] = i;
		top = k;
	}
	rt = sta[1];
	dfs(rt);
	bool flag = 1;
	for(int i = 1; i <= n; ++i){
		if(!any[i]) flag = 0;
	}
	if(flag){
		printf("%d %lld\n", m, 1ll * (m + 1) * m / 2);
		return;
	}
	ll sum = 0; 
	for(int i = 0; i <= s[rt].tot; ++i){
		sum += s[rt].to[i];
		//fprintf(stderr,"%d\n", it);
	}
	printf("%d %lld\n", s[rt].tot + 1, sum);sky;
}
int main(){
#ifdef LOCAL
	file(a);
#endif
	int T; read(T);
	while(T--){
		solve();
		fprintf(stderr, "%dms\n", clock() );
	}
	return 0;
}

标签:ch,gcd,ICPC2024,Divisible,Make,int,le,区间,define
From: https://www.cnblogs.com/cbdsopa/p/18570939

相关文章

  • 【Linux学习】(7)项目自动化构建工具make/Makefile
    Linux项目自动化构建工具-make/Makefile1.背景介绍会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文......
  • Linux笔记---Makefile的简单用法
    1.什么是MakefileMakefile是一种用于自动化构建和管理项目的工具,特别是在软件开发中非常常见。它包含了一系列规则(rules)和指令,描述了如何编译和链接源代码文件,以及生成最终的可执行文件或库文件。简单来说,在系统中存在一个叫做make的命令,该命令被使用之后,会在当前目录下......
  • QtCreator通过CMake多文件编译.cpp、.qss、.h、.ui文件,达到MVC三层架构的效果
        博主在构建C++项目的时候,一般都喜欢将头文件和源文件分开为不同的文件夹,比如include目录下只存放.h文件和.ui文件,src目录下只存放.cpp和.qss文件,res目录下只存放图片、音频等文件,这时候使用CMake对项目进行分文件管理就特别方便和清晰了。  很多人写qt项目的......
  • [C++]在windows基于C++编程署yolov11-cls的openvino图像分类模型cmake项目部署演示源
    【算法介绍】在Windows系统上,基于C++编程部署YOLOv11-CLS的OpenVINO图像分类模型,可以通过CMake项目来实现。以下是简要介绍:首先,需要准备开发环境,包括安装OpenVINOToolkit、CMake、OpenCV和C++编译器(如GCC或MSVC)。OpenVINO是英特尔开发的一款用于优化和部署深度学习模型的工具套件,......
  • 【Golang】踩坑记录:make()创建引用类型,初始值是不是nil!!
    文章目录起因二、得记住的知识点1.make()切片,初始化了吗?2.make()切片不同长度容量,append时的差别3.切片是指向数组的指针吗?4.切片扩容时,重新分配内存,原切片的数据怎么办?三、咳咳,总结一下起因序列化的时候居然给我空指针报错,哪nil啦???猛一顿查,查到了创建的结构体......
  • Makefile入门学习过程中的一些知识点-一些常见规则或语法:
    1.order-only依赖:还是以上一篇的sudoku项目为例,之前写的目标之后的依赖都属于普通依赖,普通依赖都对应自身的规则,order-only依赖也是一样的,但是当依赖文件中的内容发生改动的时候,两种依赖就会产生差别:对于普通依赖而言,当依赖发生改变需要重新与目标文件生成链接,也就是说如果任......
  • Makefile
    Makefile是由target和命令构成的,最简单的Makefile:build: gcctest.c-otest然后执行makebuild就会执行gcc这条命令,但是一般推荐先将源文件构建为对象文件,然后再统一编译为可执行文件build:test.o gcctest.o-otesttest.o: gcctest.c-c文件目标test.o是build伪......
  • cmake使用方法
    CMake是一个跨平台的构建系统生成器,广泛用于C++项目。它允许开发者编写平台无关的构建脚本(称为`CMakeLists.txt`),然后在不同的平台上生成对应的构建文件(如Makefile、VisualStudio项目文件等)。以下是使用CMake的基本步骤和一些常见的用法。 ###安装CMake首先,你需......
  • cmakelist 源码生成so 文件 orthanc mysql
    cmakelist.txt#Orthanc-ALightweight,RESTfulDICOMStore#Copyright(C)2012-2016SebastienJodogne,MedicalPhysics#Department,UniversityHospitalofLiege,Belgium#Copyright(C)2017-2023OsimisS.A.,Belgium#Copyright(C)2024-2024Orthanc......
  • CMake 属性之目录属性
     【写在前面】        CMake的目录属性是指在特定目录(及其子目录)范围内有效的设置。        这些属性不同于全局变量或目标(Target)属性,它们提供了一种机制,允许开发者为项目中的不同部分定义不同的构建行为。        通过目录属性,你可以指定编译器......