一个班上有 n
n
个学生,每个学生都有一个 id 和四门课的成绩,每个学生的 id
i
d
为 1
1
到 n 之间的整数,每个学生的 id
i
d
都不相同,将所有学生按总分从高到低排序,若总分相同则按 id
i
d
从小到大排序,问 id
i
d
为 1
1
的学生最终排在第几名。
输入
第一行为一个整数 n(1≤n≤1000),接下去 n
n
行每行 4 个整数 ai,bi,ci,di(0≤ai,bi,ci,di≤100)
a
i
,
b
i
,
c
i
,
d
i
(
0
≤
a
i
,
b
i
,
c
i
,
d
i
≤
100
)
,第 i
i
行的 4 个整数表示 id
i
d
号为 i
i
的学生四门课的成绩。
将 n
n
分成 m 块3,其中每一块中的数字是递增的,各个块之间是递减的,则最长上升子序列的长度和最长下降子序列的长度和最小,为 m+⌈nm⌉
m
+
⌈
n
m
⌉
,由基本不等式可知,当 m
m
为 n−−√ 时 m+⌈nm⌉
m
+
⌈
n
m
⌉
的值最小。
过题代码
#include <iostream>#include <cstdio>#include <cstdlib>#include <cmath>#include <climits>#include <cstring>#include <string>#include <vector>#include <list>#include <queue>#include <stack>#include <map>#include <set>#include <bitset>#include <algorithm>#include <functional>#include <iomanip>usingnamespacestd;
#define LL long longconstint maxn = 100000 + 100;
int n;
int num[maxn];
int main() {
#ifdef LOCAL
freopen("test.txt", "r", stdin);
// freopen("test1.out", "w", stdout);#endif // LOCAL
ios::sync_with_stdio(false);
while(scanf("%d", &n) != EOF) {
int Index = sqrt(n);
int tmp = 1;
int Min = n;
for(int i = n - Index; i >= 0; i -= Index) {
Min = i;
for(int j = 0; j < Index; ++j) {
num[i + j] = tmp++;
}
}
for(int i = 0; i < Min; ++i) {
num[i] = tmp++;
}
for(int i = 0; i < n; ++i) {
if(i != 0) {
printf(" ");
}
printf("%d", num[i]);
}
printf("\n");
}
return0;
}
D. The Wu
题意
有一个元素个数为 m
m
的 01 串集合,集合内的 01
01
串可以重复出现,且每一个串的长度都为 n
n
,对于两个 01 串 s
s
和 t,它们产生的价值为 vs,t=∑ni=1(si==ti?wi:0)
v
s
,
t
=
∑
i
=
1
n
(
s
i
==
t
i
?
w
i
:
0
)
,现在有 q
q
次询问,每次询问由一个长度为 n 的 01
01
串 s
s
和一个数 k 组成,询问集合中满足条件 vs,t≤k
v
s
,
t
≤
k
的字符串个数。
输入
第一行为 3
3
个整数 n,m,q(1≤n≤12,1≤q,m≤5×105)。第二行为 n
n
个整数 w1,w2,⋯,wn(0≤wi≤100),接下去 m
m
行每行一个长度为 n 的 01
01
字符串,接下去 q
q
行每行由一个长度为 n 的 01
01
字符串和 k(0≤k≤100)
k
(
0
≤
k
≤
100
)
组成。
先将字符串转化为对应数字(0
0
到 2n−1),对于集合中的所有数字 t
t
,统计每个数字出现的次数 cntt,接着对所有可能的询问 s
s
(0 到 2n−1
2
n
−
1
)都预处理出对应的价值 vs,t
v
s
,
t
,将价值为 vs,t
v
s
,
t
的集合中的数字 t
t
的个数记录在 sum[s][vs,t] 中,即 sum[s][vs,t]=cntt
s
u
m
[
s
]
[
v
s
,
t
]
=
c
n
t
t
,由于询问中的 k
k
最大值为 100,所以 sum
s
u
m
数组的第 2
2
维只需要开到 100,大于 100
100
的可以不用处理,最后对 sum
s
u
m
的每一次询问 s
s
都做一遍前缀和,就能得到询问为 s 时,价值小于等于 k
k
的所有满足条件的 t 的个数。预处理和查询的时间复杂度为 O(m+2n×2nn+100×2n+qn)
O
(
m
+
2
n
×
2
n
n
+
100
×
2
n
+
q
n
)
。
过题代码
#include <iostream>#include <cstdio>#include <cstdlib>#include <cmath>#include <climits>#include <cstring>#include <string>#include <vector>#include <list>#include <queue>#include <stack>#include <map>#include <set>#include <bitset>#include <algorithm>#include <functional>#include <iomanip>usingnamespacestd;
#define LL long longint n, m, k, q;
char str[20];
int num[20], cnt[4096];
int sum[4096 + 100][100 + 20];
int main() {
#ifdef LOCAL
freopen("test.txt", "r", stdin);
// freopen("test1.out", "w", stdout);#endif // LOCAL
ios::sync_with_stdio(false);
while(scanf("%d%d%d", &n, &m, &q) != EOF) {
memset(cnt, 0, sizeof(cnt));
memset(sum, 0, sizeof(sum));
for(int i = 0; i < n; ++i) {
scanf("%d", &num[i]);
}
for(int i = 0; i < m; ++i) {
scanf("%s", str);
int tmp = 0;
for(int j = 0; j < n; ++j) {
tmp |= (str[j] - '0') << j;
}
++cnt[tmp];
}
for(int i = 0; i < (1 << n); ++i) {
for(int j = 0; j < (1 << n); ++j) {
int tmp = 0;
for(int k = 0; k < n; ++k) {
if(((i >> k) & 1) == ((j >> k) & 1)) {
tmp += num[k];
}
}
if(tmp > 100) {
continue;
}
sum[i][tmp] += cnt[j];
}
}
for(int i = 0; i < (1 << n); ++i) {
for(int j = 1; j <= 100; ++j) {
sum[i][j] += sum[i][j - 1];
}
}
for(int i = 0; i < q; ++i) {
scanf("%s%d", str, &k);
int tmp = 0;
for(int j = 0; j < n; ++j) {
tmp |= (str[j] - '0') << j;
}
printf("%d\n", sum[tmp][k]);
}
}
return0;
}
E. The Supersonic Rocket
题意
有两个点集,点的个数分别为 n
n
和 m,问两个点集分别构成的凸包是否可以通过旋转、平移使得两个凸包完全重合。
输入
第一行包含两个整数 n,m(3≤n,m≤105)
n
,
m
(
3
≤
n
,
m
≤
10
5
)
,接下去 n
n
行每行两个整数 xi,yi,表示第一个点集中每个点的坐标,最后 m
m
行每行两个整数 xi,yi,表示第二个点集中每个点的坐标,其中 0≤xi,yi≤108
0
≤
x
i
,
y
i
≤
10
8
。
输出
如果可以通过旋转与平移使两个凸包完全重合,输出 YES
Y
E
S
,否则输出 NO
N
O
,大小写任意。
样例
输入
3 4 0 0 0 2 2 0 0 2 2 2 2 0 1 1
输出
YES
提示
点集在平面直角坐标系上的位置为:
将第一个点集旋转 180°
180
°
后:
再将第二个点集平移 (−2,−2)
(
−
2
,
−
2
)
后:
输入
3 4 0 0 0 2 2 0 0 2 2 2 2 0 0 0
输出
NO
提示
不论如何平移旋转,两个凸包都不会重合。
题解
先分别跑出两个点集的凸包,判定两个凸包可以旋转平移重合的条件是:每条对应边的长度相等且对应角的大小相等,对应边直接求边长平方,对应角可以用叉积。将其中一个点集在凸包上所有点都复制到最后,让另一个凸包的所有点在这上面跑一遍 kmp
k
m
p
,kmp
k
m
p
能够往后跑的条件就是对应边与对应角都相等。
f(x)=Ax3+Bx2+Cx+Dexlogf(1)=0exlogf(pa11pa22⋯pakk)=a1f(p1)+a2f(p2)+⋯akf(pk)
f
(
x
)
=
A
x
3
+
B
x
2
+
C
x
+
D
e
x
l
o
g
f
(
1
)
=
0
e
x
l
o
g
f
(
p
1
a
1
p
2
a
2
⋯
p
k
a
k
)
=
a
1
f
(
p
1
)
+
a
2
f
(
p
2
)
+
⋯
a
k
f
(
p
k
)
设
n=pa11pa22⋯pakk
n
=
p
1
a
1
p
2
a
2
⋯
p
k
a
k
,其中的每个
p
p
是 n 的质因数,
a
a
值是质因数对应的指数。
给定整数 n 和参数
A,B,C,D
A
,
B
,
C
,
D
的值,计算以下公式:
∑i=1nexlogf(i)
∑
i
=
1
n
e
x
l
o
g
f
(
i
)
输入
输入包含 4
4
个整数 n,A,B,C,D(1≤n≤3×108,0≤A,B,C,D≤106)。
输出
将计算结果对 232
2
32
取模后输出。
样例
输入
12 0 0 1 0
输出
63
输入
4 1 2 3 4
输出
136
题解
从 1
1
到 n 内每个质数 p
p
对答案的贡献为 ⌊np⌋+⌊np2⌋+⋯⌊npk⌋,其中 k
k
为满足 pk≤n 的最大值,因此最后的答案就是:
∑i=1nf(pi)(⌊npi⌋+⌊np2i⌋+⋯⌊npki⌋)
∑
i
=
1
n
f
(
p
i
)
(
⌊
n
p
i
⌋
+
⌊
n
p
i
2
⌋
+
⋯
⌊
n
p
i
k
⌋
)
对
232
2
32
取模,就是用
unsigned int 进行计算并让它们自然溢出。
最后是内存限制,如果我们用一个线性筛,就需要存每个数字是否是质数,如果用
bool 数组,
3×108
3
×
10
8
的
bool 数组需要
286MB
286
M
B
,用
bitset 压位需要
2868≈35.75MB
286
8
≈
35.75
M
B
,如果用埃氏筛,就可以在筛的过程中跳过
2
2
和 3 的倍数,我们将删去
2
2
和 3 的倍数后连续的数字用对应的
1,2,...,n
1
,
2
,
.
.
.
,
n
表示:
5
5
7
11
11
13
13
17
17
19
19
23
23
25
25
29
29
31
31
35
35
⋯
⋯
1
1
2
3
3
4
5
5
6
7
7
8
9
9
10
11
11
⋯
⋯
可以发现数字与下标之间是 Index=⌊n3⌋
I
n
d
e
x
=
⌊
n
3
⌋
的关系,因此如果跳过 2
2
和 3 的倍数,可以减少 23
2
3
的空间,最后只需要 35.753≈11.9MB
35.75
3
≈
11.9
M
B
。
过题代码
#include <iostream>#include <cstdio>#include <cstdlib>#include <cmath>#include <climits>#include <cstring>#include <string>#include <vector>#include <list>#include <queue>#include <stack>#include <map>#include <set>#include <bitset>#include <algorithm>#include <functional>#include <iomanip>usingnamespacestd;
#define LL long longunsigned n, a, b, c, d;
unsigned ans;
bitset<100000001> bit;
unsigned f(unsigned x) {
return a * x * x * x + b * x * x + c * x + d;
}
unsigned Get(unsigned x) {
unsigned ret = f(x);
unsigned tmp = 0;
for(unsigned i = 1; i <= n / x; i *= x) {
tmp += n / (i * x);
}
return ret * tmp;
}
int main() {
#ifdef LOCAL
freopen("test.txt", "r", stdin);
// freopen("test1.out", "w", stdout);#endif // LOCAL
ios::sync_with_stdio(false);
while(cin >> n >> a >> b >> c >> d) {
ans = Get(2) + Get(3);
bit.reset();
for(unsigned i = 5; i <= n; ++i) {
if(i % 2 == 0 || i % 3 == 0) {
continue;
}
if(bit[i / 3] == 0) {
ans += Get(i);
for(unsigned j = i; j <= n / i; ++j) {
if(i * j % 2 == 0 || i * j % 3 == 0) {
continue;
}
bit[i * j / 3] = 1;
}
}
}
cout << ans << endl;
}
return0;
}