A. 越狱
题意:
给定一个长度为
n
n
n的序列
a
a
a,找到一个最小的正整数
x
x
x,使得
m
i
n
(
∑
i
=
1
n
[
a
i
>
x
]
,
∑
i
=
1
n
[
a
i
<
x
]
)
min(\sum\limits_{i=1}^n[a_i>x],\sum\limits_{i=1}^n[a_i<x])
min(i=1∑n[ai>x],i=1∑n[ai<x])最大化。
数据范围:
1
≤
n
≤
2
×
1
0
5
,
1
≤
a
i
≤
2
×
1
0
9
1\leq n\leq 2\times 10^5,1\leq a_i\leq 2\times 10^9
1≤n≤2×105,1≤ai≤2×109
对于
1
≤
i
<
n
1\leq i<n
1≤i<n,都有
a
i
+
1
<
a
i
+
1
a_i+1<a_{i+1}
ai+1<ai+1
题解:
找到排名为
n
2
\frac{n}{2}
2n的数即可
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int a[N], n;
int c[N];
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
nth_element(a, a + n / 2 - 1, a + n);
printf("%d %d\n", n / 2, a[n / 2 - 1] + 1);
return 0;
}
B. 物流
题意:
A地有
a
a
a 吨货物,B地有
b
b
b 吨货物,C地需要
c
c
c 吨货物,D地需要
d
d
d 吨货物。
1
1
1吨货物从A到C花
a
c
ac
ac的代价,从A到D花
a
d
ad
ad的代价,从B到C花
b
c
bc
bc的代价,从B到D花
b
d
bd
bd的代价。求最小代价。
注意,从A/B地到C/D地的货物是一吨一吨运输的,也就是说你可以选择A/B地的一部分货物来满足C/D地的一部分需求,但是最终C/D的需求必须全部满足。
输入只含有正整数。
数据范围:
T
T
T组数据,
1
≤
T
≤
1
0
5
1\leq T\leq 10^5
1≤T≤105
1
≤
a
,
b
,
c
,
d
,
a
c
,
a
d
,
b
c
,
c
d
≤
2
×
1
0
9
1\leq a,b,c,d,ac,ad,bc,cd\leq 2\times 10^9
1≤a,b,c,d,ac,ad,bc,cd≤2×109
a
+
b
=
c
+
d
a+b=c+d
a+b=c+d
题解:
考虑
a
c
,
a
d
,
b
c
,
b
d
ac, ad, bc, bd
ac,ad,bc,bd
如果
m
a
x
(
a
c
,
a
d
)
−
m
i
n
(
a
c
,
a
d
)
>
m
a
x
(
b
c
,
b
d
)
−
m
i
n
(
b
c
,
b
d
)
max(ac, ad) - min(ac, ad) > max(bc, bd) - min(bc, bd)
max(ac,ad)−min(ac,ad)>max(bc,bd)−min(bc,bd)
那么必然是优先处理掉
m
i
n
(
a
c
,
a
d
)
min(ac, ad)
min(ac,ad),这样使得少一点支出,
所以这里只需要考虑优先处理掉
a
c
,
a
d
,
b
c
,
b
d
ac,ad,bc,bd
ac,ad,bc,bd中的一种即可。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve(int ca) {
ll a, b, c, d, v[5];
scanf("%lld%lld%lld%lld%lld%lld%lld%lld", &a, &b, &c, &d, &v[1], &v[2], &v[3], &v[4]);
ll res = 9e18;
// a -> c
{
if(a >= c) {
res = min(res, c * v[1] + (a - c) * v[2] + b * v[4]); // a->c, a->d, b->d
} else if(a < c) {
res = min(res, a * v[1] + (c - a) * v[3] + d * v[4]); // a->c, b->c, b->d
}
}
// a -> d
{
if(a >= d) {
res = min(res, d * v[2] + (a - d) * v[1] + b * v[3]); // a->d, a->c, b->d
} else if(a < d) {
res = min(res, a * v[2] + (d - a) * v[4] + c * v[3]); // a->d, b->d, b->c
}
}
// b -> c
{
if(b >= c) {
res = min(res, c * v[3] + (b - c) * v[4] + a * v[2]); // b->c, b->d, a->d
} else if(b < c) {
res = min(res, b * v[3] + (c - b) * v[1] + d * v[2]); // b->c, a->c, a->d
}
}
// b -> d
{
if(b >= d) {
res = min(res, d * v[4] + (b - d) * v[3] + a * v[1]); // b->d, b->c, a->c
} else if(b < d) {
res = min(res, b * v[4] + (d - b) * v[2] + c * v[1]); // b->d, a->d, a->c
}
}
printf("%lld\n", res);
}
int main()
{
int T = 1;
scanf("%d", &T);
for(int i = 1; i <= T; ++i) solve(i);
return 0;
}
C. 数的和与积
题意:
给定一个
n
n
n ,请你将
1
1
1 到
n
n
n 中的所有整数划分为两个集合,使得第一个集合里的数的积等于第二个集合里的数的和。 输出第一个集合的数。无解输出
−
1
-1
−1。
数据范围:
T
T
T组数据,
1
≤
T
≤
1
0
5
1\leq T\leq 10^5
1≤T≤105
1
≤
n
≤
1
0
9
1\leq n\leq 10^9
1≤n≤109
题解:
模拟几个样例可以发现:
n
=
2
n=2
n=2和
n
=
4
n=4
n=4时无解,
n
=
3
n=3
n=3时可以分为 sum组
1
、
2
1、2
1、2,mul组
3
3
3
从
n
=
5
n=5
n=5开始,
存在有:
5
mul: 1 2 4
6
mul: 1 2 6
7
mul: 1 3 6
8
mul: 1 3 8
9
mul: 1 4 8
10
mul: 1 4 10
11
mul: 1 5 10
12
mul: 1 5 12
可以发现三个数字依次为:
- 1 1 1
- ⌊ n − 1 2 ⌋ \lfloor\frac{n-1}{2}\rfloor ⌊2n−1⌋
- n − ( n & 1 ) n-(n\&1) n−(n&1)
证明:
(
a
+
1
)
×
(
b
+
1
)
=
a
b
+
a
+
b
+
1
(a+1)\times (b+1)=ab+a+b+1
(a+1)×(b+1)=ab+a+b+1
移项:
(
a
+
1
)
×
(
b
+
1
)
−
a
−
b
−
1
=
a
b
×
1
(a+1)\times (b+1)-a-b-1=ab\times 1
(a+1)×(b+1)−a−b−1=ab×1
因此只要找到
a
a
a和
b
b
b满足
a
+
b
+
1
+
a
b
=
n
×
(
n
+
1
)
2
a+b+1+ab=\frac{n\times(n+1)}{2}
a+b+1+ab=2n×(n+1)
n
×
(
n
+
1
)
2
\frac{n\times(n+1)}{2}
2n×(n+1)必然是一个整数,
当
n
n
n是偶数,
a
+
1
=
n
2
a+1=\frac{n}{2}
a+1=2n 和
b
+
1
=
n
+
1
b+1=n+1
b+1=n+1
a
=
n
2
−
1
a=\frac{n}{2}-1
a=2n−1,
b
=
n
b=n
b=n
当
n
n
n是奇数,
a
+
1
=
n
+
1
2
a+1=\frac{n+1}{2}
a+1=2n+1 和
b
+
1
=
n
b+1=n
b+1=n
a
=
n
+
1
2
−
1
a=\frac{n+1}{2}-1
a=2n+1−1,
b
=
n
−
1
b=n-1
b=n−1
综合起来就是 a = ⌊ n − 1 2 ⌋ , b = n − ( n & 1 ) a=\lfloor\frac{n-1}{2}\rfloor,b=n-(n\&1) a=⌊2n−1⌋,b=n−(n&1)
当
n
=
2
n=2
n=2时,
a
=
0
,
b
=
2
a=0,b=2
a=0,b=2,
a
=
0
a=0
a=0,显然不合法
当
n
=
4
n=4
n=4时,
a
=
1
,
b
=
4
a=1,b=4
a=1,b=4,因为本身就需要选择一个
1
1
1,所以这也不合法
当
n
=
3
n=3
n=3时,
a
=
1
,
b
=
2
a=1,b=2
a=1,b=2,此时再选择一个
1
1
1,这里就不合法了,但是可以只选择一个
3
3
3,这里的情况不符合公式,需要特判。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
void solve() {
scanf("%d", &n);
if(n == 2 || n == 4) {
puts("-1");
return ;
}
if(n == 3) {
puts("1\n3");
return ;
}
printf("3\n1 %d %d\n", (n - 1) / 2, (n % 2 ? n - 1 : n));
}
int main()
{
int T = 1;
scanf("%d", &T);
for(int i = 1; i <= T; ++i) solve();
return 0;
}
D. 礼物
题意:
给定一棵
n
n
n个点的树,确定一个根使得
∑
i
=
1
n
∑
j
=
1
n
L
C
A
(
i
,
j
)
\sum\limits_{i=1}^n \sum\limits_{j=1}^n LCA(i,j)
i=1∑nj=1∑nLCA(i,j) 最大。
题目保证答案唯一。
数据范围:
1
≤
n
≤
1
0
6
1\leq n\leq 10^6
1≤n≤106
题解:
换根
d
p
dp
dp
这里先考虑以
1
1
1为根进行
d
f
s
dfs
dfs,求出这样的一棵有根树的子树大小。
考虑以
1
1
1为根的有根树
- d o w n [ u ] down[u] down[u]表示 u u u的子树中的任意两点的LCA值之和
- s i z [ u ] siz[u] siz[u]表示 u u u的子树大小,包括 u u u
- 可以注意到的是树上任意一条边的两点
u
u
u和
v
v
v为根时,两者的
∑
i
=
1
n
∑
j
=
1
n
L
C
A
(
i
,
j
)
\sum\limits_{i=1}^n \sum\limits_{j=1}^n LCA(i,j)
i=1∑nj=1∑nLCA(i,j) 差别在于:
- 以
u
u
u为根时,
v及其子树
到u
、oth子树
、u的父亲fa对应的除去子树u的剩余子树
对应的权值为: s i z [ v ] × ( n − s i z [ v ] ) × u siz[v]\times (n-siz[v])\times u siz[v]×(n−siz[v])×u - 以
v
v
v为根时,
u
、oth子树
、u的父亲fa对应的除去子树u的剩余子树
到v及其子树
对应的权值为: s i z [ v ] × ( n − s i z [ v ] ) × v siz[v]\times (n-siz[v])\times v siz[v]×(n−siz[v])×v
两者的其余部分是完全相同的,分别为:
- 以
u
u
u为根时,
- d o w n [ v ] down[v] down[v]
- d o w n [ o t h ] down[oth] down[oth]
- ∑ s o n ∈ f a [ s o n ≠ u ] d o w n [ s o n ] \sum\limits_{son \in fa} [son \ne u]down[son] son∈fa∑[son=u]down[son]
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1000010;
const int M = N << 1;
int h[N], e[M], ne[M], idx;
int n, id;
ll sum, ans;
ll siz[N];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs1(int u, int fa) {
siz[u] = 1;
for(int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if(v != fa) {
dfs1(v, u);
sum += siz[u] * siz[v] * u;
siz[u] += siz[v];
}
}
}
void dfs2(int u, int fa) {
if(sum > ans) {
ans = sum;
id = u;
}
for(int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if(v != fa) {
ll temp = siz[v] * (n - siz[v]) * (u - v);
sum -= temp;
dfs2(v, u);
sum += temp;
}
}
}
void solve(int ca) {
scanf("%d", &n);
memset(h, -1, (n + 1) * sizeof(int));
idx = 0;
for(int i = 1; i < n; ++i) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
add(b, a);
}
dfs1(1, -1);
ans = 0;
dfs2(1, -1);
printf("%d %lld\n", id, ans * 2 + 1ll * n * (n + 1) / 2);
}
int main()
{
int T = 1;
// scanf("%d", &T);
for(int i = 1; i <= T; ++i) solve(i);
return 0;
}