7.30模考总结

省流:上300了 (模考难度不大,橙黄绿蓝)

\(7.30\)

\(T1\) 报数游戏Ⅱ

题意简述

求在一段序列前加入一个最小的正整数,使这个序列的每一个前缀和都为正数。

思路:

前缀和扫一遍,找最小前缀和,特判为正数的情况,复杂度 \(O(n)\)

\(code\)

#pragma G++ optimize(3, "Ofast", "inline")
#pragma G++ optimize(2)
#include <iostream>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <cmath>
#include <queue>
#include <set>
#include <climits>
#include <random>
#include <bitset>
#include <unordered_map>
#define C(y,x) fac[y]/fac[(y)-(x)]/fac[x]
#define _max(a,b) (a>b?a:b)
#define _min(a,b) (a<b?a:b)
#define orz %
#define ll long long
#define int long long
#define juruo Optimist_Skm
#define mid (l + r >> 1)
#define pii std::pair<int, int>
#define fi first
#define se second
#define eb emplace_back
#define pb push_back
#define m_p std::make_pair
#define pq std::priority_queue<int>
#define pq_min_std::priority_queue<int, std::vector<int>, std::greater<int> >
#define pq_min_pii std::priority_queue<pii, std::vector<pii>, std::greater<pii> >
#define open(x) freopen(#x".in", "r", stdin);freopen(#x".out", "w", stdout);
#define test(x) cout << "Test: " << x << '\n'
#define close fclose(stdin);fclose(stdout);
#define ull unsigned long long
#define debug(); printf("Skmqwq\n");

namespace Fast_Skm {

	template <typename T>
	inline void read(T &x) {
		T s = 0, w = 1;
 		char c = getchar();
		while(!isdigit(c)) {
			if(c == '-')  w  = -1;
			c = getchar();
		}
		while(isdigit(c))
			s = (s << 1) + (s << 3) + (c & 0xcf), c = getchar();
			
		x = s * w;
		return ;
	}

	template <typename T, typename... Arp>
	inline void read(T &x, Arp &...arp) {
		read(x), read(arp...);
        return ;
	}

	template <typename T>
	inline void write(T x) {
		if(x < 0) x = -x, putchar('-');
		int p = 0;
		static char s[100];
		do {
			s[++p] = x orz 10 + '0';
			x /= 10;
		} while (x);
		while(p) putchar(s[p--]);
		putchar(' ');
	}

	template <typename T, typename... Arp>
	inline void write(T &x, Arp &...arp) {
		write(x), write(arp...);
		return ;
	}

	template <typename S, typename T>
	inline void smax(S &x, T y) {
		x = (x > y) ? x : y;
	}

	template <typename S, typename T>
	inline void smin(S &x, T y) {
		x = (x < y) ? x : y;
	}

	inline void quit() {
		exit(0);
		return ;
	}
	
	inline ll quick_pow(ll a, ll b, ll P) {
		ll ans = 1;
		while(b >= 1) {
			if(b & 1) {
				ans = ans * a % P;
			}
			a = a * a % P;
			b >>= 1;
		}
		return ans;
	}
	
	template <typename T>
	inline T exgcd(T a, T b, T &x, T &y) {
		if(b == 0) {
		 	x = 1; y = 0;
		 	return a;
		}
		int gcd = exgcd(b, a % b, x, y);
		int tmp = y;
		y = x - a / b * y;
		x = tmp;
		return gcd;
	}

} using namespace Fast_Skm;

const int N = 1e5 + 7;
int n, a[N];
ll sum[N];

signed main() {

	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);

	read(n);
	for (int i = 1; i <= n; ++i) {
		read(a[i]);
	}

	for (int i = 1; i <= n; ++i) {
		sum[i] = sum[i - 1] + a[i];
	}

    ll ans = sum[1];
	for (int i = 2; i <= n; ++i) {
		smin(ans, sum[i]);
	}

	if (ans > 0) {
		write(1);
	} else {
		write(-ans + 1);
	}

	return 0;
}

本题总结:

要保证签到题的 \(AC\)

\(T2\) 百万富翁的第二次实验

题意简述:

求一段序列里最长的一段区间,使这段区间里的元素互不重复。

思路:

值域是 \(10^{11}\) ,但序列长度只有 \(10^5\) ,所以可以想到离散化,然后就变成一道双指针的模版,直接把左右指针向左做同向运动即可,复杂度 \(O(n)\)

\(code\)

#pragma G++ optimize(3, "Ofast", "inline")
#pragma G++ optimize(2)
#include <iostream>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <cmath>
#include <queue>
#include <set>
#include <climits>
#include <random>
#include <bitset>
#include <unordered_map>
#define C(y,x) fac[y]/fac[(y)-(x)]/fac[x]
#define _max(a,b) (a>b?a:b)
#define _min(a,b) (a<b?a:b)
#define orz %
#define ll long long
#define int long long
#define juruo Optimist_Skm
#define mid (l + r >> 1)
#define pii std::pair<int, int>
#define fi first
#define se second
#define eb emplace_back
#define pb push_back
#define m_p std::make_pair
#define pq std::priority_queue<int>
#define pq_min_std::priority_queue<int, std::vector<int>, std::greater<int> >
#define pq_min_pii std::priority_queue<pii, std::vector<pii>, std::greater<pii> >
#define open(x) freopen(#x".in", "r", stdin);freopen(#x".out", "w", stdout);
#define test(x) cout << "Test: " << x << '\n'
#define close fclose(stdin);fclose(stdout);
#define ull unsigned long long
#define debug(); printf("Skmqwq\n");

namespace Fast_Skm {

	template <typename T>
	inline void read(T &x) {
		T s = 0, w = 1;
 		char c = getchar();
		while(!isdigit(c)) {
			if(c == '-')  w  = -1;
			c = getchar();
		}
		while(isdigit(c))
			s = (s << 1) + (s << 3) + (c & 0xcf), c = getchar();
			
		x = s * w;
		return ;
	}

	template <typename T, typename... Arp>
	inline void read(T &x, Arp &...arp) {
		read(x), read(arp...);
        return ;
	}

	template <typename T>
	inline void write(T x) {
		if(x < 0) x = -x, putchar('-');
		int p = 0;
		static char s[100];
		do {
			s[++p] = x orz 10 + '0';
			x /= 10;
		} while (x);
		while(p) putchar(s[p--]);
		putchar(' ');
	}

	template <typename T, typename... Arp>
	inline void write(T &x, Arp &...arp) {
		write(x), write(arp...);
		return ;
	}

	template <typename S, typename T>
	inline void smax(S &x, T y) {
		x = (x > y) ? x : y;
	}

	template <typename S, typename T>
	inline void smin(S &x, T y) {
		x = (x < y) ? x : y;
	}

	inline void quit() {
		exit(0);
		return ;
	}
	
	inline ll quick_pow(ll a, ll b, ll P) {
		ll ans = 1;
		while(b >= 1) {
			if(b & 1) {
				ans = ans * a % P;
			}
			a = a * a % P;
			b >>= 1;
		}
		return ans;
	}
	
	template <typename T>
	inline T exgcd(T a, T b, T &x, T &y) {
		if(b == 0) {
		 	x = 1; y = 0;
		 	return a;
		}
		int gcd = exgcd(b, a % b, x, y);
		int tmp = y;
		y = x - a / b * y;
		x = tmp;
		return gcd;
	}

} using namespace Fast_Skm;

const int N = 1e6 + 7;
ll n, a[N], t[N], b[N];

signed main() {

	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);

	read(n);
	for (int i = 1; i <= n; ++i)
		read(a[i]), b[i] = a[i];

	std::sort(b + 1, b + 1 + n); //离散化
	for (int i = 1; i <= n; ++i) {
		int p = std::lower_bound(b + 1, b + 1 + n, a[i]) - b;
		a[i] = p;
	}

	/*for (int i = 1; i <= n; ++i) {
		write(a[i]);
	}*/

	int l = 1, r = 1, ans = 0;
	t[a[1]]++;
	while (r < n) { //双指针
		while (t[a[r + 1]] == 0 && r < n) {
			t[a[r + 1]]++;
			r++;
		}
		smax(ans, r - l + 1);
		while (t[a[r + 1]] != 0 && l <= r + 1) {
			t[a[l]]--;
			l++;
		}
	}

	write(ans);

	return 0;
}

本题总结:

本题在 \(S\) 组模拟赛中偏简单,可以一眼看出,但是要保证代码中不出现失误,最好要进行对拍。

\(T3\) magic

题意简述:

有一段数列,和一段排列,问最少从数列中取多少个数和排列中的出相乘,得到的和,能大于等于 \(q\)

思路:

可以贪心的思考,每次去数列和排列中最大的两个数相乘。

观察到排列是一个公差为 \(1\) 的等差数列,我们可以考虑维护一个这样的数组

\[doublesum_N= 1 \times a_N, \ doublesum_{N-1} = 2 \times a_N + 1 \times a_{N-1} …… ,doublesum_1 = \sum_{i=1}^N i \times a_i \]

观察不难发现,此数组正是对后缀和求后缀和的结果。

有了这个数组,我们对于每个 \(p \le N\) 可以用 \(doublesum\) 数组快速地求出来。

但是,如果 \(p > N\)

我们可以再仔细的观察 \(doublesum\) 数组,可以发现当 \(p > N\) 时,我们的数组是这样的 \(\downarrow\)

\[doublesum_1 = \sum_{i=1}^N (i+p-N) \times a_i \]

使用乘法分配律把 \(p - N\) 拆出来,得:

\[doublesum_1 = \sum_{i=1}^N i \times a_i + \sum_{i=1}^N (p - N) \times a_i \]

我们发现对于每一个 \(a_i\),它乘上的数都是固定的,不会发生变化,即 \((p - N)\) ,所以我们可以维护一个后缀和数组,每次当 \((p - N)\) 我们就可以用 \(sum\)\(doublesum\)\(O(1)\) 地求出它们的和了。

但是,这有这么用呢,我们要求的不是最少的个数吗?

此问题我们可以转化为,让值最大,取得个数最少,很明显是一道二分板子题,我们可以二分次数,用两个数组快速求出它能带来的贡献,判断合不合法即可,复杂度 \(O(n+mlogn)\)

\(code\)

#pragma G++ optimize(3, "Ofast", "inline")
#pragma G++ optimize(2)
#include <iostream>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <cmath>
#include <queue>
#include <set>
#include <climits>
#include <random>
#include <bitset>
#include <unordered_map>
#define C(y,x) fac[y]/fac[(y)-(x)]/fac[x]
#define _max(a,b) (a>b?a:b)
#define _min(a,b) (a<b?a:b)
#define orz %
#define ll long long
#define int long long
#define juruo Optimist_Skm
#define mid (l + r >> 1)
#define pii std::pair<int, int>
#define fi first
#define se second
#define eb emplace_back
#define pb push_back
#define m_p std::make_pair
#define pq std::priority_queue<int>
//#define pq_min_std::priority_queue<int, std::vector<int>, std::greater<int> >
#define pq_min_pii std::priority_queue<pii, std::vector<pii>, std::greater<pii> >
#define open(x) freopen(#x".in", "r", stdin);freopen(#x".out", "w", stdout);
#define test(x) cout << "Test: " << x << '\n'
#define close fclose(stdin);fclose(stdout);
#define ull unsigned long long
#define debug(); printf("Skmqwq\n");

namespace Fast_Skm {

	template <typename T>
	inline void read(T &x) {
		T s = 0, w = 1;
 		char c = getchar();
		while(!isdigit(c)) {
			if(c == '-')  w  = -1;
			c = getchar();
		}
		while(isdigit(c))
			s = (s << 1) + (s << 3) + (c & 0xcf), c = getchar();
			
		x = s * w;
		return ;
	}

	template <typename T, typename... Arp>
	inline void read(T &x, Arp &...arp) {
		read(x), read(arp...);
        return ;
	}

	template <typename T>
	inline void write(T x) {
		if(x < 0) x = -x, putchar('-');
		int p = 0;
		static char s[100];
		do {
			s[++p] = x orz 10 + '0';
			x /= 10;
		} while (x);
		while(p) putchar(s[p--]);
		putchar('\n');
	}

	template <typename T, typename... Arp>
	inline void write(T &x, Arp &...arp) {
		write(x), write(arp...);
		return ;
	}

	template <typename S, typename T>
	inline void smax(S &x, T y) {
		x = (x > y) ? x : y;
	}

	template <typename S, typename T>
	inline void smin(S &x, T y) {
		x = (x < y) ? x : y;
	}

	inline void quit() {
		exit(0);
		return ;
	}
	
	inline ll quick_pow(ll a, ll b, ll P) {
		ll ans = 1;
		while(b >= 1) {
			if(b & 1) {
				ans = ans * a % P;
			}
			a = a * a % P;
			b >>= 1;
		}
		return ans;
	}
	
	template <typename T>
	inline T exgcd(T a, T b, T &x, T &y) {
		if(b == 0) {
		 	x = 1; y = 0;
		 	return a;
		}
		int gcd = exgcd(b, a % b, x, y);
		int tmp = y;
		y = x - a / b * y;
		x = tmp;
		return gcd;
	}

} using namespace Fast_Skm;

const int N = 5e5 + 7;
ll n, m;
ll a[N], difsum[N], sum[N];

inline ll min(ll a, ll b) {
	return a < b ? a : b;
}

inline ll BinarySearch(ll p, ll q) {
	int l = 1, r = min(p, n), best = -1;
	while (l <= r) {
		if (difsum[mid] + 1ll * (p - mid) *  sum[mid] >= q) {
			best = mid;
			r = mid - 1;
		} else {
			l = mid + 1;
		}
	}
	return best;
}

signed main() {

	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);

	read(n, m);
	for (int i = 1; i <= n; ++i) {
		read(a[i]);
	}

	std::sort(a + 1, a + 1 + n);

	memset(difsum, 0, sizeof(difsum));
	memset(sum, 0, sizeof(sum));
	
	for (int i = n; i >= 1; --i) {
		sum[i] = sum[i + 1] + a[i];
		difsum[i] = difsum[i + 1] + sum[i];
	}

	std::sort(difsum + 1, difsum + 1 + n);
	std::sort(sum + 1, sum + 1 + n);

	//write(difsum[n], sum[n]);

	/*for (int i = 1; i <= n; ++i) {
		write(difsum[i], sum[i]);
		puts("\n");
	}*/

	for (int i = 1; i <= m; ++i) {
		ll p, q;
		read(p, q);
		write(BinarySearch(p, q));
	}

	return 0;
}

本题总结:

这是一道思维题,有一定的思维难度,但只要耐心的去找规律,推式子,还是有机会做出来的(就像这次

\(T4\) 统计区间

题意简述:

很清真的题面,我就直接复制了:

\[有一个长度为n的数列a,a的值在[1,n] 中,现在要统计区间中出现的数都恰好出现2次的区间数。 \]

思路:

考试的时候没想出来,打的暴力也爆零了

寄!

考完之后找神仙室友看了一下,他竟然想出了比正解复杂度更低的算法。(前因:我们寝床被小孩哥摇塌了,被迫搬到另一个寝,但没想到那个寝里全是省选班的大佬 比Syx强多了

正解思路:

可以维护在遍历的过程中维护每个数最新出现的位置,以及次新出现的位置,因为只能出现两次,所以当第三个数出现,次新位置的那个数就必须被弹出,这就可以转化为一道线段树扫描线的板子题了,复杂度 \(O(nlogn)\)

更优思路:

一段区间,就是一段前缀的后缀,我们可以给每个数一个随机的 \(hash\) 值,在遍历的过程中,维护每个前缀的异或和。
为什么会想到异或和呢,因为异或有一个性质,即异或一次将会是那个数的值,异或两次就刚好消掉,我们可以通过此性质,对于每一段前缀的异或值,用一个 \(unordered_map\) 进行储存,对于当前的前缀,我们求出它的异或值后,只要让答案加上与它异或值相同的前缀的个数即可。

证明

如果两个前缀的异或值完全相同,那么他们的异或和肯定等于 \(0\) ,而这个 \(0\) 就代表这两段前缀和中间的区间的异或值为 \(0\) (因为这两段前缀异或和异或起来,会把小的那段前缀给消掉,自然就剩下中间的那段区间 (和普通前缀和一样)),而这段区间的异或值等于 \(0\) ,就表示它里面所有的元素都出现了偶数次。

但是,题目要求的是两次啊

我们可以借鉴抄袭 正解的做发,维护每个数最新出现的位置,以及次新出现的位置,在遍历的过程中,不断把不合法区间带来的贡献减掉(在 \(unordered \_map\) 操作),这样,就可以保证我们这段区间中所有的数都有且仅有两个,复杂度 \(O(n)\)

\(code\)

#pragma G++ optimize(3, "Ofast", "inline")
#pragma G++ optimize(2)
#include <iostream>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <cmath>
#include <queue>
#include <set>
#include <climits>
#include <random>
#include <bitset>
#include <unordered_map>
#include <time.h>
#define C(y,x) fac[y]/fac[(y)-(x)]/fac[x]
#define _max(a,b) (a>b?a:b)
#define _min(a,b) (a<b?a:b)
#define orz %
#define ll long long
#define int long long
#define juruo Optimist_Skm
#define mid (l + r >> 1)
#define pii std::pair<int, int>
#define fi first
#define se second
#define eb emplace_back
#define pb push_back
#define m_p std::make_pair
#define pq std::priority_queue<int>
#define pq_min_std::priority_queue<int, std::vector<int>, std::greater<int> >
#define pq_min_pii std::priority_queue<pii, std::vector<pii>, std::greater<pii> >
#define open(x) freopen(#x".in", "r", stdin);freopen(#x".out", "w", stdout);
#define test(x) cout << "Test: " << x << '\n'
#define close fclose(stdin);fclose(stdout);
#define ull unsigned long long
#define debug(); printf("Skmqwq\n");

namespace Fast_Skm {

	template <typename T>
	inline void read(T &x) {
		T s = 0, w = 1;
 		char c = getchar();
		while(!isdigit(c)) {
			if(c == '-')  w  = -1;
			c = getchar();
		}
		while(isdigit(c))
			s = (s << 1) + (s << 3) + (c & 0xcf), c = getchar();
			
		x = s * w;
		return ;
	}

	template <typename T, typename... Arp>
	inline void read(T &x, Arp &...arp) {
		read(x), read(arp...);
        return ;
	}

	template <typename T>
	inline void write(T x) {
		if(x < 0) x = -x, putchar('-');
		int p = 0;
		static char s[100];
		do {
			s[++p] = x orz 10 + '0';
			x /= 10;
		} while (x);
		while(p) putchar(s[p--]);
		putchar(' ');
	}

	template <typename T, typename... Arp>
	inline void write(T &x, Arp &...arp) {
		write(x), write(arp...);
		return ;
	}

	template <typename S, typename T>
	inline void smax(S &x, T y) {
		x = (x > y) ? x : y;
	}

	template <typename S, typename T>
	inline void smin(S &x, T y) {
		x = (x < y) ? x : y;
	}

	inline void quit() {
		exit(0);
		return ;
	}
	
	inline ll quick_pow(ll a, ll b, ll P) {
		ll ans = 1;
		while(b >= 1) {
			if(b & 1) {
				ans = ans * a % P;
			}
			a = a * a % P;
			b >>= 1;
		}
		return ans;
	}
	
	template <typename T>
	inline T exgcd(T a, T b, T &x, T &y) {
		if(b == 0) {
		 	x = 1; y = 0;
		 	return a;
		}
		int gcd = exgcd(b, a % b, x, y);
		int tmp = y;
		y = x - a / b * y;
		x = tmp;
		return gcd;
	}

} using namespace Fast_Skm;

const int N = 1e6 + 7;
int n, a[N], hash1[N], last[N], se_last[N], hash2[N];
std::mt19937_64 Skmqwq(time(0) ^ clock());
std::unordered_map<int, int> mp;

signed main() {

	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);

	read(n);
	for (int i = 1; i <= n; ++i) {
		read(a[i]);
	}
	for (int i = 1; i <= n; ++i) {
		hash1[i] = Skmqwq();
	}

	mp[0] = 1;
	int pos = 0, ans = 0;
	for (int i = 1; i <= n; ++i) {
		while (pos < se_last[a[i]]) mp[hash2[pos]]--, ++pos;
		se_last[a[i]] = last[a[i]], last[a[i]] = i;
		hash2[i] = hash2[i - 1] ^ hash1[a[i]];
		ans += mp[hash2[i]];
		mp[hash2[i]]++;
	}

	write(ans);

	return 0;
}

本题总结:

这是一道线段树的板子题,但是我对线段树的了解还不深,也想不出更优的解法,太废了,对于这种题目,要保证暴力的正确性,可行性,同时多做卡常等优化,争取能取得更多的分数。

考试总结:

本次考试偏简单,但是也要稳重的对待,稳扎稳打,也要加深对各个算法变式的了解,增强思维强度。

可不要辜负自己的努力啊,2024还有机会在等着我呢 ——Syx

posted @ 2024-07-30 18:32  Optimist_Skm  阅读(37)  评论(0)    收藏  举报