2022.9.2———HZOI【CSP-S模拟1】游寄

$ Write\ In\ Front $

这次考试三道题打的都是暴力。。或者子任务分治一类。。

临近csp-s但是考场上没 \(A\) 题是很危险的标志啊。。

T1:斐波那契 T2:数颜色 T3:分组

\(成绩综述\)

image

$ Rank:\ 38 / 42 $,我菜菜

\(T1\) 究极暴力建树
\(T2\) 简单前缀和还挂了\(20pts\)
\(T3\) 胡了一个 \(XIN队\) 上去好像还打挂了

期望得分 \(70 + 30 + 10 = 110\)
但是最后就如图所示 \(70 + 10 + 8 = 88\)

$\mathfrak{T1}\ 斐波那契 $

这个题的数据范围给我的第一印象是打表找规律、数学

似乎确实是这样的,但是并不是 \(O(1)\) 的数学。。。

首先明确一下,\(f(61)\) 就已经达到了 \(10^{12}\) 的级别(我这里的斐波那契数列 \(f_1 = f_2 = 1\))

对于某一个兔子\(x\),我们尝试考虑他的父亲会是谁

把树画出来,我们可以发现神奇的地方(慢慢画,画的比原题给的树多一个月就行),电脑上实在是太难画了,所以各位草稿纸上请

首先胡一个斐波那契数列上去,毕竟题目这么写的

\[ 1\ \ 1\ \ 2\ \ 3\ \ 5\ \ 8\ \ 13\ \ 21 \]

然后仔细观察

\[ \mathbf{1} \ \ \mathbf{1} \ \ \mathbf{2} \ \ \mathbf{3} [\ 4\ ] \mathbf{5} [\ 6\ 7\ ] \mathbf{8} [\ 9\ 10\ 11\ 12\ ] \mathbf{13} [\ 14\ 15\ 16\ 17\ 18\ 19\ 20\ ] \mathbf{21}...... \]

(不是特别好看谔谔)

(其中标粗体的是斐波那契数列的数)

然后和树有啥关系呢,我们注意到,有这么一些对父子: 4和1; $\ \ $ 6和1,7和2; $\ \ $ 9和1,10和2,11和3,12和4\(......\)

然而他栗然地发了大冷。

所以 打表出结论:

  • 对于 \(i \ge 5\) ,有:\((f_{i-1}, f_i)\) 这个开区间内的数字,他们的父亲依次是 \(1,2,......,f_i-f_{i-1}-1\)。(我这里的斐波那契数列 \(f_1 = f_2 = 1\) 奥)

  • 对于 \(\text{Fibonacci}\) 数的父亲,可以发现对于 \(i \ge 3\), \(f_i\) 的父亲为 \(f_{i-2}\) 。也是看出来的。(请务必注意我这里指的斐波那契数列 \(f_1 = f_2 = 1\))

更准确地描述:

对于一个兔子编号为\(x\) :

  • \(x\) 为斐波那契数且不为 \(1\):设 \(plc\)\(x\) 在斐波那契数列中的下标,他的父亲为 \(f_{plc-2}\)

  • \(x\) 不为斐波那契数:设 \(plc\)\(x\) 在斐波那契数列中第一个小于 \(x\) 的斐波那契数的下标,则 \(x\) 的父亲为 \(x-f_{plc}\)

按照这个规律将 \(10^6\) 以内的数全部扫一遍然后暴力建树,然后暴力建树的 \(70pts\) 就到手啦~

当然了这个规律也是正解的一部分。正解是 \(a_i\)\(b_i\) 往上跳父亲,然后最先跳到同一个父亲就是答案。正确性显然。

考虑如何实现:

首先找 \(plc\) 我们很容易就能想到对斐波那契数列进行二分查找,因为斐波那契数列是单增的。用数组的 lower_bound 即可实现。手写二分也行。

判断\(x\)是否为斐波那契数的话,直接判断 f[plc] == x 就行,没必要像我这个sb一样开一个 unordered_map 然后 \(T\)

注意到对于每一组询问 \((a_i, b_i)\),不妨设 \(b_i > a_i\)(编号)。

  • 如果 \(b_i\)\(a_i\) 的子树上,显然是让 \(b_i\) 往上跳才能跳到正确答案 \(a_i\)

  • 如果不在,那么是谁先跳都无妨。

那么显然我们直接都优先让编号更大的先跳不就完了。。。

至此本题就基本做完了,代码实现也很简单。这个题主要的为了阅读体验我把之前代码和最终代码分开放吧

T1
#include <iostream>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout);
#define re register int
#define char_phi signed
#define MARK cout << "###"
#define MARKER "@@@"
#define LMARK "!!!~~~"
#define ZY " qwq "
#define _ ' '
#define Endl cout << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL), cerr.tie(NULL);}
/*
	向同深度的跳跃。
	浮华。
	唱,跳,rap,篮球,music.
*/
long long Q, ai, bi, faa, plca;
long long f[65];
void work(){
	cin >> Q;
	f[1] = f[2] = 1;
	for (re i = 3 ; i <= 61 ; ++ i)
		f[i] = f[i-1] + f[i-2];
	while (Q --){
		cin >> ai >> bi;
		while (ai != bi){
			if (ai < bi)
				swap(ai, bi);
			plca = lower_bound(f+2, f+61+2, ai)-f-1;
			if (ai == f[plca])
				plca ++, ai = f[plca-2];
			else 
				ai = ai-f[plca];
		}
		cout << ai << '\n';
	}
}
// #define IXINGMY
char_phi main(){
    #ifdef IXINGMY
        FBI_OPENTHEDOOR(a);
    #endif
    Fastio_setup();
    work();
    return GMY;
}
重构1
%:pragma GCC optimize(3)
#include <iostream>
#include <unordered_map>
#include <set>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout);
#define re register int
#define char_phi signed
#define MARK cout << "###"
#define MARKER "@@@"
#define LMARK "!!!~~~"
#define ZY " qwq "
#define _ ' '
#define Endl cout << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define N 300005
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL), cerr.tie(NULL);}
char isa, isb;// is_fibonacci
long long Q, ai, bi, plca, plcb, faa, fbb, final_ans;
long long f[65];
unordered_map<long long, bool> mp;
// multiset<long long, greater<long long>>::iterator it;
void work(){
	cin >> Q;
	f[0] = f[1] = f[2] = 1;
	for (re i = 3 ; f[i-1] <= 1000000000000ll ; ++ i)
		f[i] = f[i-1]+f[i-2], mp[f[i]] = true;
	/*	for (re i = 1 ; i <= 61 ; ++ i)
		cout << f[i] << '\n';*/
	while (Q --){
		//if (Q <= 200000)
			//cerr << "很勇嘛," << '\n';
		// cerr << "您在哪超时的???" << '\n';
		cin >> ai >> bi;
		if (ai < bi)
			swap(ai, bi);
		faa = ai, fbb = bi; final_ans = 0;
		multiset<long long, greater<long long>> a, b;
		// a.clear(), b.clear();
		a.insert(ai), b.insert(bi);
		// cerr << '\n' << '\n' << '\n';
		while (faa != fbb){
			// sleep(1), cerr << "Here we are, Nick Of Time!" << '\n';
			plca = lower_bound(f+2, f+60+2, ai)-f-1, plcb = lower_bound(f+2, f+60+2, bi)-f-1;
			if (mp[ai] == true)
				plca ++, faa = f[plca-2];
			else 
				faa = ai-f[plca];
			if (mp[bi] == true)
				plcb ++, fbb = f[plcb-2];
			else 
				fbb = bi-f[plcb];
			sleep(1), cerr << ai << _ << plca << _ << faa << _ << _ << _ << bi << _ << plcb << _ << fbb << '\n';
			ai = faa, bi = fbb;
		}
		while (faa != 1){
			plca = lower_bound(f+2, f+60+2, ai)-f-1;
			if (mp[ai] == true)
				plca ++, faa = f[plca-2];
			else 
				faa = ai-f[plca];
			ai = faa;
			a.insert(faa);
		}
		while (fbb != 1){
			plcb = lower_bound(f+2, f+60+2, bi)-f-1;
			if (mp[bi] == true)
				plcb ++, fbb = f[plcb-2];
			else 
				fbb = bi-f[plcb];
			bi = fbb;
			b.insert(fbb);
		}
		for (auto it : a)
			if (*b.lower_bound(it) == it)
				{final_ans = it; break;}
		/*for (it = prev(a.end()) ; it != a.begin() ; it = prev(it))
			if (b.find(*it) != b.end())
				break;*/
		if (final_ans == 0)
			final_ans = 1;
		cout << final_ans << '\n';
	}
}
// #define IXINGMY
char_phi main(){
	#ifdef IXINGMY
		FBI_OPENTHEDOOR(a);
	#endif
    Fastio_setup();
    work();
    return GMY;
}
重构2
#include <iostream>
#include <unordered_map>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout);
#define re register int
#define char_phi signed
#define MARK cout << "###"
#define MARKER "@@@"
#define LMARK "!!!~~~"
#define ZY " qwq "
#define _ ' '
#define Endl cout << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define N 300005
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL), cerr.tie(NULL);}
char isa, isb;// is_fibonacci
long long Q, ai, bi, plca, plcb, faa, fbb, final_ans, id;
long long f[65], a[45];
unordered_map<long long, bool> mp;
// multiset<long long, greater<long long>>::iterator it;
/*
	开心地把multiset的T掉之后就来写数组的
	思路很明确
	开搞
*/
inline int jiangxu_lower_bound(long long x){
	long long l(1), r(a[0]), mid;
	while (l <= r){
		mid = ((l+r)>>1);
		// if (mid < 0 or l < 0 or r < 0)
			// cerr << "WTF" << '\n';
		// if (mid > 40)
			// cerr << mid << '\n';
		if (x > a[mid])
			r = mid-1;
		else 
			l = mid+1;
	}
	// cout << "Binary " << l << _ << r << _ << mid << '\n';
	return r;
}
// 956722026041
// 1548008755920
// 999377885466
// 1000000000000
void work(){
	cin >> Q;
	f[0] = f[1] = f[2] = 1;
	for (re i = 3 ; f[i-1] <= 1000000000000ll ; ++ i)
		f[i] = f[i-1]+f[i-2], mp[f[i]] = true;
	/*for (re i = 1 ; i <= 61 ; ++ i)
		cerr << f[i] << '\n';*/
	while (Q --){
		// cerr << Q << '\n';
		//if (Q <= 200000)
			//cerr << "很勇嘛," << '\n';
		// cerr << "您在哪超时的???" << '\n';
		cin >> ai >> bi;
		if (ai < bi)
			swap(ai, bi);
		// cerr << ai << _ << bi << '\n';
		faa = ai, fbb = bi; final_ans = a[0] = 0;
		a[++ a[0]] = ai;
		while (faa != 1){// 看来这里出现了RE
			plca = lower_bound(f+2, f+61+2, ai)-f-1;// yes +61!
			// if (Q == 299913)
				// cerr << ai << _ << plca << _ << faa << _ << a[0] << '\n';
			// if (Q == 299913 and a[0] > 10000)
				// cerr << "WTF" << '\n';
			if (mp[ai] == true)
				plca ++, faa = f[plca-2];
			else 
				faa = ai-f[plca];
			ai = faa;
			a[++ a[0]] = faa;
			// if (a[0] > 40)
				// cerr << a[0] << '\n';
		}
		// if (Q == 299913)
			// {sleep(10), cerr << "Ivebenn there" << '\n';}// 嘿嘿,找RE好方法
		if (a[0] == 1)
			{cout << 1 << '\n'; continue;}
		/*MARK;
		for (re i = 1 ; i <= a[0] ; ++ i)
			cout << a[i] << _;
		Endl;*/
		id = jiangxu_lower_bound(fbb);
		if (a[id] == fbb and id != a[0]+1 and id != 0)
			{final_ans = fbb; goto chu;}
		while (fbb != 1){
			plcb = lower_bound(f+2, f+61+2, bi)-f-1;
			if (mp[bi] == true)
				plcb ++, fbb = f[plcb-2];
			else 
				fbb = bi-f[plcb];
			id = jiangxu_lower_bound(fbb);
			if (a[id] == fbb and id != a[0]+1 and id != 0)
				{final_ans = fbb; break;}
			// cout << "fbb:" << fbb << _ << id << _ << a[id] << '\n';
			bi = fbb;
		}
		chu:{
			cout << final_ans << '\n';
		}
	}
}
// #define IXINGMY
char_phi main(){
	#ifdef IXINGMY
		FBI_OPENTHEDOOR(a);
	#endif
    Fastio_setup();
    work();
    return GMY;
}

叨一句我踩的坑,我刚开始是把\(a_i\)\(b_i\)的全部父亲都分别扔到两个set里然后反向遍历找答案,然后\(T\)飞。不过卡卡常仍然可以拿到\(80pts\)的好成绩,再往上就不清楚行不行了

$ \mathfrak{T2}\ 数颜色 $

这个题啊,可以发现如果说暴力的话数组是显然开不下的

想一个能开得下数组的方法

分块。

然后手动开了一下发现 a[550][300005] 还真的开得下,然后浅算一下660011000B = 629.44MiB,谔谔,似乎会 \(MLE\),但是据说评测机看的是实际内存占用,显然 \(a\) 数组不会全占满的罢

然后就开始写了

\(\mathscr{UPD}\)\(\text{€€£}\) 好像并不是只看实际内存占用,所以尽量别开这么大!!!

然后就暴力维护了。暴力统计每个块中某个颜色的个数,然后查询的时候直接查就完了

但是发现了一些问题。如果块长为 \(\sqrt{n}\) 的话,是会 \(TLE\)

我们尝试扩大块长

然后扩来扩去发现 pow(n, 0.625) 挺不错的然后直接 \(A\) 掉(其实是看到学长的题解这么说的才知道(

T2
%:pragma GCC optimize(3)
#include <iostream>
#include <cmath>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout);
#define re register int
#define char_phi signed
#define MARK cout << "###"
#define MARKER "@@@"
#define LMARK "!!!~~~"
#define ZY " qwq "
#define _ ' '
#define Endl cout << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define N 300005
#define SQ 550
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL), cerr.tie(NULL);}
/*
	看来我的前缀和想法没错
	只不过分一下块
	呃呃不对
	直接统计块内某个颜色的个数
	nsqrt(n)大约过去应该行
*/
int n, Q, blocknum, blocklen;
int L[SQ], R[SQ], col[N], belong[N];
int a[SQ][N];
inline int Query(int x, int y, int z){
	int res(0);
	if (belong[x] == belong[y]){
		for (re i = x ; i <= y ; ++ i)
			if (col[i] == z)
				res ++;
		return res;
	}
	else {
		for (re i = x ; i <= R[belong[x]] ; ++ i)
			if (col[i] == z)
				res ++;
		for (re i = L[belong[y]] ; i <= y ; ++ i)
			if (col[i] == z)
				res ++;
		for (re i = belong[x]+1 ; i <= belong[y]-1 ; ++ i)
			res += a[i][z];
		return res;
	}
}
inline void Update(int x){
	if (belong[x] == belong[x+1]){
		swap(col[x], col[x+1]);
	}
	else {
		a[belong[x]][col[x]] --, a[belong[x+1]][col[x]] ++;
		a[belong[x+1]][col[x+1]] --, a[belong[x]][col[x+1]] ++;
		swap(col[x], col[x+1]);
	}
}
void work(){
	cin >> n >> Q;
	for (re i = 1 ; i <= n ; ++ i)
		cin >> col[i];// sb了,col都没读呢你怎么加的。。。
	blocklen = pow(n, 0.625); blocknum = n/blocklen;
	for (re i = 1 ; i <= blocknum ; ++ i){
		L[i] = R[i-1]+1, R[i] = L[i]+blocklen-1;
		for (re j = L[i] ; j <= R[i] ; ++ j)
			belong[j] = i, a[i][col[j]] ++;
		// cout << L[i] << _ << R[i] << '\n';
	}
	if (blocklen*blocknum < n){
		blocknum ++, L[blocknum] = R[blocknum-1]+1, R[blocknum] = n;
		for (re i = L[blocknum] ; i <= R[blocknum] ; ++ i)
			belong[i] = blocknum, a[blocknum][col[i]] ++;
	}
	// cerr << blocknum << _ << blocklen << '\n';
	// cout << blocknum << _ << L[blocknum] << _ << R[blocknum] << '\n';
	int opt, x, y, z;
	while (Q --){
		cin >> opt;
		if (opt == 1){
			cin >> x >> y >> z;
			cout << Query(x, y, z) << '\n';
		}
		else {
			cin >> x;
			Update(x);
		}
	}
}
// #define IXINGMY
char_phi main(){
    #ifdef IXINGMY
        FBI_OPENTHEDOOR(a);
    #endif
    Fastio_setup();
    work();
    return GMY;
}

$ \mathfrak{T3}\ 分组 $

这题主要就是一个贪心,但是我贺的题解

贪心我不会证(((

对于 \(K = 1\):

  • 为了满足组数最少并且字典序最小,从后往前拓展组数,如果新加进来的一个数和之前这个组里的数有冲突,那么这个位置就该断掉然后新设立一个组。

  • \(x\) 为当前选择是否要加入的数,如果 [\(某个平方数-x\)] 这个值存在过,那么当前 \(x\) 加入当前组就不合法。也就是要断掉然后新设立一个组。

  • 考虑如何判断是否存在过。考虑极限数据,之前一个数为 \(131072\),当前新加的这个数也是 \(131072\),相加正好为 \(512^2\)。所以(应该是这样 )从 \(1 \sim 512\) 循环枚举平方数 \(j \times j\),判断 used[j*j-col[i]] == true 即可。然后判断完了别忘了加进去 used[j*j-col[i]] = true

对于 \(K = 2\):

  • 这时候每个组都有了一对冲突的机会,也就是说,每个组里可以存在两个\(\mathbf{小团体内部互不冲突}\)的小团体

“这启发了我”———狗屁不通文章生成器

  • 还是从后往前尝试加入元素,但是如果当前元素加入后小组中出现了三个及以上的小团体,那就该在这个位置断开。

  • 然后就可以用拓展域并查集维护了。

说实话的,这个题我也是不太懂。。尤其是拓展域并查集这块不会用,所以贺了一篇题解((

T3
#include <iostream>
#include <vector>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x) freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout);
#define re register int
#define char_phi signed
#define MARK cout << "###"
#define MARKER "@@@"
#define LMARK "!!!~~~"
#define ZY " qwq "
#define _ ' '
#define Endl cout << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define N 131101
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL), cerr.tie(NULL);}
/*
	似乎是听懂了
*/
int n, K, lst;// 循环清空数组
char used[N<<1];
int col[N], fa[N<<1];
vector<int> ans, v[N<<1];
inline int find(int x){return ((fa[x] == x) ? (x) : (fa[x] = find(fa[x])));}
void work(){
	cin >> n >> K;
	for (re i = 1 ; i <= n ; ++ i)
		cin >> col[i];
	lst = n+1;
	if (K == 2)
		goto work2;
	// K = 1
	for (re i = n ; i >= 1 ; -- i){
		for (re j = 1 ; j <= 512 ; ++ j)
			if (j*j >= col[i])
				if (used[j*j-col[i]] == true)
					{goto CANT;}
		used[col[i]] = true;
		continue;
		CANT:{
			for (re j = i+1 ; j < lst ; ++ j)
				used[col[j]] = false;
			ans.push_back(i); lst = i+1;
			used[col[i]] = true;
		}
	}
	cout << ans.size()+1 << '\n';
	for (re i = ans.size()-1 ; i >= 0 ; -- i)
		cout << ans[i] << _;
	Endl;
	return ;
	work2:{// K = 2
		for (re i = 1 ; i <= (n<<1) ; ++ i)
			fa[i] = i;
		for (re i = n, tag, F ; i >= 1 ; -- i){
			for (re j = 1 ; j <= 512 ; ++ j){
				tag = j*j-col[i];
				if (tag >= 0){
					//  cerr << tag << '\n';
					if (v[tag].size() != 0){
					// cerr << "谁  他  妈  买  R  E" << '\n';
						for (re k = 0 ; k < v[tag].size() ; ++ k){
							F = v[tag][k];
							if (find(F) == find(i)){// 这个位置加入后当前小组内出现了三个及以上的敌对团体 这个位置无法加入
								for (re h = i+1 ; h < lst ; ++ h)
									vector<int>().swap(v[col[h]]);
								ans.push_back(i); lst = i+1;
								break;
							}
							else {
								fa[find(i+n)] = find(F);// 拓展域并查集
								fa[find(F+n)] = find(i);
							}
						}
					}
				}
			}
			v[col[i]].push_back(i);
		}
		cout << ans.size()+1 << '\n';
		for (re i = ans.size()-1 ; i >= 0 ; -- i)
			cout << ans[i] << _;
		Endl;
	}
}
// #define IXINGMY
char_phi main(){
    #ifdef IXINGMY
        FBI_OPENTHEDOOR(a);
    #endif
    Fastio_setup();
    work();
    return GMY;
}
posted @   char_phi  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
点击右上角即可分享
微信分享提示