20190908 模拟赛题解

当然,我承认这是我打的最差的模拟赛了。

T1 四个质数和:

【题意】:给定了一个正整数 N。有多少种方法将 N 分解成为四个质数 a , b , c , d a,b,c,d a,b,c,d的和。
例如: 9 = 2 + 2 + 2 + 3 = 2 + 2 + 3 + 2 = 2 + 3 + 2 + 2 = 3 + 2 + 2 + 2 9 = 2 + 2 + 2 + 3 = 2 + 2 + 3 + 2 = 2 + 3 + 2 + 2 = 3 + 2 + 2 + 2 9=2+2+2+3=2+2+3+2=2+3+2+2=3+2+2+2,故共有 4 4 4 种方法将 9 9 9分解成为四个整数。
N ≤ 100000 N\leq100000 N100000
【输入格式】
本题多组数据测试:
第一行读入一个整数 T T T 表示数据组数。接
下来共 T T T 行,每行包含一个正整数 N N N
【输出格式】
T T T 行,每行一个整数表示答案。
【输入样例】
2
9
10
【输出样例】
4
6
【数据范围】
对于 10 % 10% 10的数据, N ≤ 10 N≤10 N10
对于 40 % 40% 40的数据, N ≤ 100 N≤100 N100
对于 70 % 70% 70的数据, N ≤ 1000 N≤1000 N1000
对于 100 % 100% 100的数据, T ≤ 10 , N ≤ 100000 T≤10,N≤100000 T10N100000
【题解】:
40 40 40分:筛素数+背包
70 70 70分:先枚举两个素数和,求出每个的方案数再枚举两个和 O ( N 2 ) O(N^{2}) O(N2)
100 100 100分:70分加双指针
但是我得了50分
【考试代码】:

#include<bits/stdc++.h>
using namespace std;

const int MAX = 1e5 + 10;
int t, a[MAX], maxx;
int v[MAX], prime[MAX / 10];
int tot;
int ans;

inline int read() {
	int s = 0, f = 1;
	char ch;
	for(; ch < '0' || ch > '9'; ch = getchar())	if(ch == '-')	f = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar())	s = (s << 1) + (s << 3) + ch - '0';
	return s * f;
}

inline void euler() {
	memset(v, 0, sizeof(v));
	tot = 0;
	for(int i = 2; i <= maxx; ++i) {
		if(!v[i]) {
			v[i] = i;
			prime[++tot] = i;
		}
		for(int j = 1; j <= tot; ++j) {
			if(prime[j] > v[i] || prime[j] > maxx / i)	break;
			v[i * prime[j]] = prime[j];
		}
	}
}

inline void find(int x) {
	for(int a = 1; a <= tot; ++a) {
		if(prime[a] > x)	break;
		for(int b = 1; b <= tot; ++b) {
			if(prime[b] > x)	break;
			for(int c = 1; c <= tot; ++c) {
				if(prime[c] > x)	break;
				for(int d = 1; d <= tot; ++d) {
					if(prime[d] > x)	break;
					if(prime[a] + prime[b] + prime[c] + prime[d] == x)
						++ans;
				}
			}
		}
	}
		
	printf("%d\n", ans);
}

int main() {
	freopen("plus.in", "r", stdin);
	freopen("plus.out", "w",stdout);
	t = read();
	for(int i = 1; i <= t; ++i)	a[i] = read(), maxx = max(maxx, a[i]);
	euler();
	for(int i = 1; i <= t; ++i) {
		ans = 0;
		find(a[i]);
	}
	return 0;
}

标程:

#include<bits/stdc++.h>
using namespace std;

const int MAX = 1e5 + 10;
int t, n;
int v[MAX], prime[MAX / 10], f[MAX];
int tot;
long long ans;

inline int read() {
	int s = 0, f = 1;
	char ch;
	for(; ch < '0' || ch > '9'; ch = getchar())	if(ch == '-')	f = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar())	s = (s << 1) + (s << 3) + ch - '0';
	return s * f;
}

inline void euler() {
	memset(v, 0, sizeof(v));
	tot = 0;
	for(int i = 2; i <= MAX; ++i) {
		if(!v[i]) {
			v[i] = i;
			prime[++tot] = i;
		}
		for(int j = 1; j <= tot; ++j) {
			if(prime[j] > v[i] || prime[j] > MAX / i)	break;
			v[i * prime[j]] = prime[j];
		}
	}
}

int main() {
	freopen("plus.in", "r", stdin);
	freopen("plus.out", "w",stdout);
	t = read();
	euler();
	for(int i = 1; i <= tot; ++i) 
		for(int j = 1; j <= tot && prime[i] + prime[j] < MAX; ++j)
			++f[prime[i] + prime[j]];
	while(t--) {
		n = read();
		for(int i = 2; i < n; ++i) {
			ans += f[i] * f[n - i];
		}
		printf("%lld\n", ans);
		ans = 0;
	}
	return 0;
}

T2匹配最大异或:

【题意】:
假设给定了两个整数 m , n m,n m,n。有 n n n 个互不相同的整数 x 1 , x 2 , . . . , x n x_1,x_2,...,x_n x1,x2,...,xn 0 ≤ x i ≤ 2 m − 1 0≤x_i≤2^m-1 0xi2m1)。对于每一个属于 0 0 0 2 m − 1 2^m-1 2m1 y y y,我们找到 p y p_y py 使得 x p y x_{p_y} xpy 异或 y y y 有最大值。即对于任意的 i≠py, 有 y ⊕ x p y &gt; y ⊕ x i y⊕x_{p_y} &gt;y⊕ x_i yxpy>yxi。(其中⊕表示二进制异或)。现在我们把这个问题反过来。给定 m m m n n n,以及序列 p 0 , p 1 , . . . , p 2 m − 1 p_0,p_1,...,p_{2^m-1} p0,p1,...,p2m1,计算有多少个不同序列 x 1 , x 2 , . . . , x n x_1,x_2,...,x_n x1,x2,...,xn 可以通过上文描述的问题生成出序列 p p p。两个序列是不同的当且仅当存在至少一个 i i i 使得两个序列中 x i x_i xi 是不同的。
答案对 1000000007( 1 0 9 10^9 109+7)取模。
【输入格式】
第一行两个用空格隔开的整数 m , n m,n m,n。其中 2 m 2^m 2m p p p 序列的长度, n n n x x x 序列的长度。
之后 2 m 2^m 2m 行,每行一个整数,表示 p p p 序列。保证 1 1 1 n n n 中的每一个数在序列 p p p 中都
至少出现一次。
【输出格式】
输出一行一个整数表示答案。
【数据范围】
对于 30 % 30% 30的数据: m ≤ 3 , n ≤ 4 m≤3,n≤4 m3n4
另外 10 % 10% 10的数据: m = 0 m=0 m=0
另外 10 % 10% 10的数据: n = 1 n=1 n=1
另外 10 % 10% 10的数据: p i = i , 2 m = n p_i=i, 2^m=n pi=i,2m=n
对于 100 % 100% 100的数据: 0 ≤ m ≤ 16 , 1 ≤ p i ≤ n , 1 ≤ n ≤ 2 m 0≤m≤16,1≤pi≤n,1≤n≤2^m 0m161pin1n2m
这题乍一看没看懂。。
题解:我们可以二分,在区间 ( l , r ) (l,r) (l,r)中的 p l , l + 1 , . . . , r p_{l,l+1,...,r} pl,l+1,...,r都在 ( l , r ) (l,r) (l,r)的范围内。
如果 x x x中既有最高位为 0 0 0,又有为 1 1 1的数时。在 ( l , m i d ) (l,mid) (l,mid) ( m i d + 1 , r ) (mid+1,r) (mid+1,r)没有相等的 p p p,那么根据乘法原理,方案数就是两个答案相乘。
如果 x x x中只有最高为 0 0 0 1 1 1的数。那么 ( l , m i d ) (l,mid) (l,mid) ( m i d + 1 , r ) (mid+1,r) (mid+1,r) p p p一定是对应相等的,而 x i x_i xi的最高位相等,所以答案应该是 ( l , m i d ) ∗ 2 (l, mid)*2 (l,mid)2。因为两个区间 ( l , m i d ) (l,mid) (l,mid) ( m i d + 1 , r ) (mid+1,r) (mid+1,r)都对应相等。

当然这里一定要有 l = r l = r l=r时,答案为 1 1 1
代码:

#include<bits/stdc++.h>
using namespace std;

const int mod = 1e9 + 7;
const int MAX = 1 << 16;
int m, n;
int p[MAX];
bool v[MAX];

inline int read() {
	int s = 0, f = 1;
	char ch;
	for(; ch < '0' || ch > '9'; ch = getchar())	if(ch == '-')	f = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar())	s = (s << 1) + (s << 3) + ch - '0';
	return s * f;
}

inline int solve(int l, int r) {
	if(l == r) return 1;
	int mid = (l + r) / 2;
	int ans = 0;
	bool flag = 1;
	for(int i = l; i <= mid; ++i)
		if(p[i] != p[mid + i - l + 1]) {
			flag = 0; 
			break;
		}
	if(flag) ans = (ans + 2LL * solve(l, mid) % mod) % mod;
	flag = 1;
	for(int i = l; i <= mid; ++i) v[p[i]] = 1;
	for(int i = mid + 1; i <= r; ++i) if(v[p[i]]) {
		flag = 0; 
		break;
	}
	for(int i = l; i <= mid; ++i) v[p[i]] = 0;
	if(flag) ans = (ans + 1LL * solve(l, mid) * solve(mid + 1, r) % mod) % mod;
	return ans;
}

int main() {
	freopen("match.in", "r", stdin);
	freopen("match.out", "w", stdout);
	m = read();
	n = read();
	for(int i = 1; i <= 1 << m; ++i) p[i] = read();
	printf("%d\n", solve(1, 1 << m));
}

T3染色相邻的边:

【题意】:
给定一个 N N N 个点的树,点的标号从 1 1 1 N N N
一条树上点 a a a 到点 b b b 的简单路径 P P P 是一个 k k k 个点的序列 ( a = P 1 , P 2 , . . . , P k = b ) (a=P_1,P_2,...,P_k=b) (a=P1,P2,...,Pk=b),相邻的
两个点之间有边连接且任何一个点至多在序列中出现一次。注意 a a a 可能和 b b b 是相等的。
简单路径上的边就是连接序列上相邻两个点的边。
一条简单路径的邻边是只有一个端点在简单路径上的边。
树上的每条边是黑色的或者白色的。最开始所有的边都是黑色的。有 Q Q Q 次操作,有两
种操作类型。
0 0 0 计算 a a a b b b 的简单路径上有多少条边是黑色的。
1 1 1 将简单路径 a a a b b b 上的边全部设置成白色的。将简单路径 a a a b b b 上的邻边设置成黑
色的。
【输入格式】
第一行一个整数 N ( 1 ≤ N ≤ 200000 ) N(1≤N≤200000) N(1N200000)
接下来 N − 1 N-1 N1 行,每行两个整数 a i , b i a_i,b_i aibi,表示一条树边。保证读入的是一棵树。接
下来一行一个整数 Q ( 1 ≤ Q ≤ 300000 ) Q(1≤Q≤300000) Q(1Q300000)
接下来 Q Q Q 行,每行三个整数 t i , a i , b i t_i, a_i, b_i ti,ai,bi。其中 t i t_i ti 表示操作类型。
【输出格式】
对于每个 0 0 0 操作,输出一行一个整数表示答案。
【样例输入】
19
1 2
2 3
1 5
5 4
5 6
6 7
6 8
1 11
11 12
11 13
11 10
10 9
13 14
13 15
15 16
15 17
15 18
15 19
6
1 19 8
0 16 2
0 16 3
1 12 9
0 19 8
0 16 9
【样例输出】
2
3
2
2
【数据范围】
对于 5 % 5% 5的数据: N = 1 N=1 N=1
对于 20 % 20% 20的数据: N ≤ 200 N≤200 N200
对于 30 % 30% 30的数据: N ≤ 2000 N≤2000 N2000
另外 20 % 20% 20的数据:树的形态是一条链
另外 30 % 30% 30的数据:操作 1 1 1 a i = b i a_i=b_i ai=bi,且 a i a_i ai 是随机生成的。
对于 100 % 100% 100的数据: 1 ≤ N ≤ 200000 , 1 ≤ Q ≤ 300000 1≤N≤200000,1≤Q≤300000 1N2000001Q300000
这题太难了,我写不出来。
当然部分分还是可以拿的:
5 分:直接输出 0 即可。
20 分:O(N^3)模拟操作即可(相对好写的暴力)。但是我写挂了
10 分:O(N^2)模拟即可(相对难写的暴力)。
另外 20 分(一条链):修改操作为单点修改,线段树维护。
100 % 100\% 100%我有一点概念性的思路。就是把每个数据分开来做,把单链的用线段树维护,以一条边为黑色的充要条件,是这条边的两个端点被不同的修改操作路径覆盖过。(好像要用树剖)我不会啊
此题无代码,我也没写
一杯茶,一包烟,一道线段树做一天。。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值