当然,我承认这是我打的最差的模拟赛了。
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
N≤100000
【输入格式】
本题多组数据测试:
第一行读入一个整数
T
T
T 表示数据组数。接
下来共
T
T
T 行,每行包含一个正整数
N
N
N。
【输出格式】
共
T
T
T 行,每行一个整数表示答案。
【输入样例】
2
9
10
【输出样例】
4
6
【数据范围】
对于
10
%
10%
10%的数据,
N
≤
10
N≤10
N≤10。
对于
40
%
40%
40%的数据,
N
≤
100
N≤100
N≤100。
对于
70
%
70%
70%的数据,
N
≤
1000
N≤1000
N≤1000。
对于
100
%
100%
100%的数据,
T
≤
10
,
N
≤
100000
T≤10,N≤100000
T≤10,N≤100000。
【题解】:
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
0≤xi≤2m−1)。对于每一个属于
0
0
0 到
2
m
−
1
2^m-1
2m−1 的
y
y
y,我们找到
p
y
p_y
py 使得
x
p
y
x_{p_y}
xpy 异或
y
y
y 有最大值。即对于任意的 i≠py, 有
y
⊕
x
p
y
>
y
⊕
x
i
y⊕x_{p_y} >y⊕ x_i
y⊕xpy>y⊕xi。(其中⊕表示二进制异或)。现在我们把这个问题反过来。给定
m
m
m 和
n
n
n,以及序列
p
0
,
p
1
,
.
.
.
,
p
2
m
−
1
p_0,p_1,...,p_{2^m-1}
p0,p1,...,p2m−1,计算有多少个不同序列
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
m≤3,n≤4
另外
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
0≤m≤16,1≤pi≤n,1≤n≤2m
这题乍一看没看懂。。
题解:我们可以二分,在区间
(
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(1≤N≤200000)。
接下来
N
−
1
N-1
N−1 行,每行两个整数
a
i
,
b
i
a_i,b_i
ai,bi,表示一条树边。保证读入的是一棵树。接
下来一行一个整数
Q
(
1
≤
Q
≤
300000
)
Q(1≤Q≤300000)
Q(1≤Q≤300000)。
接下来
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
N≤200
对于
30
%
30%
30%的数据:
N
≤
2000
N≤2000
N≤2000
另外
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
1≤N≤200000,1≤Q≤300000
这题太难了,我写不出来。
当然部分分还是可以拿的:
5 分:直接输出 0 即可。
20 分:O(N^3)模拟操作即可(相对好写的暴力)。但是我写挂了
10 分:O(N^2)模拟即可(相对难写的暴力)。
另外 20 分(一条链):修改操作为单点修改,线段树维护。
100
%
100\%
100%我有一点概念性的思路。就是把每个数据分开来做,把单链的用线段树维护,以一条边为黑色的充要条件,是这条边的两个端点被不同的修改操作路径覆盖过。(好像要用树剖)我不会啊
此题无代码,我也没写
一杯茶,一包烟,一道线段树做一天。。。。。