7.21模考总结

省流:上 200pts

7.21

模考总结:

T1 (题目链接)

题面简述:

求一段序列中有多少个子序列 (此处为连续的) 的和能被 k 整除。

考试思路:

想到整除就可以想到取模,想到取模就可以想到它的一个性质,即如果 NM (mod K) ,那么 |NM|0 (mod K) (狗都知道)

再观察题面,发现此处的子序列是连续的,我们就可以想到此题正解的一个必要技巧——前缀和。但是只有前缀和是不够的( 最多 50pts ),我们还需要一个类似于哈希的桶,来存放每一个前缀和对 k 取模的余数,然后我们就会发现,有些前缀和对 k 取模的余数竟然是相同的,那么就可以用上面的公式,求出每一个桶中每一个余数对答案的贡献。

AC code:

#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 orz %
#define ll long long
#define juruo Optimist_Skm
#define mid ((l + r) / 2.0)
#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 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("qwq\n");

namespace Fast_Skm {

	template <typename T>
	inline void read(T &x) {
		register 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('-');
		register 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) {
		register 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 = 5e4 + 7, M = 1e6 + 7;
ll T, n, k, a[N], t[M];
ll sum[N];
signed main() {

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

	//freopen("1.out", "r", stdin);
	//freopen("2.out", "w", stdout);

	read(T);

	for (int i = 1; i <= T; ++i) {
		read(k, n);
		memset(t, 0, sizeof(t));
		memset(sum, 0, sizeof(sum));
		for (int j = 1; j <= n; ++j) {
			read(a[j]);
			sum[j] = sum[j - 1] + a[j];  //预处理前缀和
		}
		for (int j = 1; j <= n; ++j) {
			t[sum[j] % k]++;       //用桶计数
		}
		ll ans = t[0];
		for (int j = 0; j < k; ++j) {
			ans += t[j] * 1ll * (t[j] - 1) / 2;  //计算对答案的贡献
		}
		write(ans);
	}
	
	return 0;
}

T2 (题目链接)

题面简述:

我们可以把树想象成若干条链的结合体,然后后我们要求的就是这几条链上严格不下降子序列(连续)的个数(不必最长)。太抽象了吧。

考试思路:

记录最大左边界与最小右边界,进行 dfs ,遍历整棵树,当发现一个儿子是合法时,在一个新图上将父亲向儿子连边,并将父亲的出度++,最后如果不合法则不连边,这样做可以保证在一个连通块内的点所在的子序列的起点是一样的废话。最后只需要在新图中找有多少个出度为 0 的点(也就是一个不下降子序列的终点)即可。
ACcode:

#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 orz %
#define ll long long
#define juruo Optimist_Skm
#define mid ((l + r) / 2.0)
#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 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("qwq\n");

namespace Fast_Skm {

	template <typename T>
	inline void read(T &x) {
		register 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('-');
		register 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) {
		register 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 = 2e5 + 7, M = 1e9 + 7;
int n, l[N], r[N], ans = 0, vis[N], out_deg[N];
std::vector<int> E[N];

inline bool cmp(int a, int b) {
	return r[a] < r[b];
}

inline void dfs(int now, int maxl, int minr) { //维护边界

	std::sort(E[now].begin(), E[now].end(), cmp);

	for (const auto &it : E[now]) {
		if(r[it] < maxl) {
			dfs(it, l[it], r[it]);
			maxl -= r[it];
			minr -= r[it];
		}
		else {
			out_deg[now]++; //记录出度
			int tmpr = std::min(maxl, minr), tmpl = std::max(l[it], maxl); //更新边界
			dfs(it, tmpl, tmpr);
		}
	}
	
}

signed main() {

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

	read(n);
	for (int i = 2; i <= n; ++i) {
		int x;
		read(x);
		E[x].eb(i);
	} 

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

	dfs(1, l[1], r[1]);

	for (int i = 1; i <= n; ++i) {
		if(out_deg[i] == 0) {
			ans++;
		}
	}
	write(ans);

	return 0;
}

T3 (题目链接)

题面简述:

一眼背包,但不会。N 物品中选若干个,其中有两个限制,一个是每类的重量上限,一个是总的重量上限,我们要求出最打价值。

订正思路:

先对每个类进行 01 背包,然后总体进行分组背包。竟然出奇地简单!!!

ACcode:

#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 orz %
#define ll long long
#define juruo Optimist_Skm
#define mid ((l + r) / 2.0)
#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 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("qwq\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 = 5e3 + 7;
ll n, m, C, v[N], w[N], c[N], dp[N][N], lim[N], f[N][N];
std::vector<pii> l[N];

signed main() {

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

	read(n, m, C);
	for (int i = 1; i <= n; ++i) {
		read(v[i], w[i], c[i]);
		l[c[i]].eb(m_p(v[i], w[i]));
	}

	for (int i = 1; i <= C; ++i) {
		read(lim[i]);
        smin(lim[i], m);
	}
	
	for (int i = 1; i <= C; ++i) {
		for (int k = 0; k < l[i].size(); ++k) {
			for (int j = lim[i]; j >= l[i][k].se; --j) {
				smax(f[i][j], f[i][j - l[i][k].se] + l[i][k].fi);
			}
		}
	}

	for (int i = 1; i <= C; ++i) {
        for (int j = 1; j <= m; ++j) dp[i][j] = dp[i - 1][j];
		for (int j = 1; j <= lim[i]; ++j) {
			for (int k = j; k <= m; ++k) {
    			smax(dp[i][k], dp[i - 1][k - j] + f[i][j]);
			}
		}
	}
	
	write(dp[C][m]);

	return 0;
}

T4 (题目链接)

题面简述:

求扭出所有种类的扭蛋的期望概率。

订正思路:

我们可以从大到小枚举每个扭蛋,因为大的扭蛋机总会扭出比小的扭蛋机扭出更多的扭蛋,对于每一个 i 扭出 i 的期望为,倒数几个扭蛋机未扭出 i 的概率乘上这个扭蛋机能扭出 i 的概率。

ACcode:

#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 orz %
#define ll long long
#define juruo Optimist_Skm
#define mid ((l + r) / 2.0)
#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 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("qwq\n");

namespace Fast_Skm {

	template <typename T>
	inline void read(T &x) {
		register 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('-');
		register 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) {
		register 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, M = 1e7 + 7, P = 19260817;
ll a[N], inv[M], 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]);
	}

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

	inv[1] = 1;
	for (int i = 2; i <= a[n]; ++i) {
		inv[i] = (P - P / i) * inv[P % i] % P;
	}

	ll ans = 0;
	for (int i = n; i >= 1; --i) {
		for (int j = a[i]; j > a[i - 1]; --j) {
			ans += (1LL * a[i] * inv[a[n] - j + 1] % P);
			ans %= P;
		}
	}

	write(ans);

	return 0;
}

总结:有些许的进步,但是像 T3 这样的板子题还是要加强对算法的理解与记忆,对于 T4 要避免出现看不懂题面的情况,增加自己的词汇量。

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

posted @   Optimist_Skm  阅读(15)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示