D - At Most 3 (Contestant ver.)
题意:
构造一个长度在
1
1
1到
300
300
300的序列
a
a
a,
1
≤
a
i
≤
1
0
6
1\leq a_i\leq 10^6
1≤ai≤106。满足在这个序列中选择
k
k
k个数
(
1
≤
k
≤
3
)
(1\leq k\leq 3)
(1≤k≤3),使得所有任选的
k
k
k个数的和可以覆盖
1
1
1到
w
w
w这个区间内的所有正整数。
题解:
这题的构造很巧妙,可以知道的是,如果
k
k
k没有限制,那么用二进制去枚举是最佳选择。
但是这道题限制了序列长度以及
a
i
a_i
ai和
w
w
w的范围。
将
1000000
1000000
1000000个数分为三组,
第一组权值范围为
[
0
,
100
]
[0,100]
[0,100],位权为
10000
10000
10000,
第二组权值范围为
[
0
,
99
]
[0,99]
[0,99],位权为
100
100
100,
第三组权值范围为
[
0
,
99
]
[0,99]
[0,99],位权为
1
1
1,
其中权值为
0
0
0代表不选,这样就使得
[
1
,
1000000
]
[1,1000000]
[1,1000000]被全覆盖。
一共选择了
298
298
298个数。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve() {
int w;
scanf("%d", &w);
printf("%d\n", 298);
printf("1000000");
for(int k = 0, temp = 1; k < 3; ++k) {
for(int i = 1; i <= 99; ++i)
printf(" %d", temp * i);
temp *= 100;
}
printf("\n");
}
int main()
{
int T = 1;
// scanf("%d", &T);
for(int i = 1; i <= T; ++i) solve();
return 0;
}
E - Takahashi and Animals
题意:
给定长度为
n
n
n的序列,
a
i
a_i
ai表示覆盖
i
i
i和
i
+
1
i+1
i+1的代价,当
i
=
n
i=n
i=n,
i
+
1
i+1
i+1表示
1
1
1
问覆盖
n
n
n个元素的最低代价是多少。
数据范围:
1
≤
n
≤
3
×
1
0
5
1\leq n\leq 3\times 10^5
1≤n≤3×105
1
≤
a
[
i
]
≤
1
0
9
1\leq a[i]\leq 10^9
1≤a[i]≤109
题解:
f
[
i
]
[
1
]
f[i][1]
f[i][1]表示选择
a
[
i
]
a[i]
a[i],覆盖前
i
i
i个元素的最小代价
f
[
i
]
[
0
]
f[i][0]
f[i][0]表示不选择
a
[
i
]
a[i]
a[i],覆盖前
i
i
i个元素的最小代价
- 当不选择
a
i
a_i
ai,
a
i
−
1
a_{i-1}
ai−1必须选择,否则第
i
i
i个元素就不会被覆盖
即 f [ i ] [ 0 ] = f [ i − 1 ] [ 1 ] f[i][0]=f[i-1][1] f[i][0]=f[i−1][1] - 当选择
a
i
a_i
ai,
a
i
−
1
a_{i-1}
ai−1可选课不选,此时第
i
i
i个元素都会被覆盖
即 f [ i ] [ 1 ] = m i n ( f [ i − 1 ] [ 0 ] , f [ i ] [ 1 ] ) + a [ i ] f[i][1] = min(f[i-1][0],f[i][1])+a[i] f[i][1]=min(f[i−1][0],f[i][1])+a[i]
这种环形问题,考虑分隔点。
以
a
[
1
]
a[1]
a[1]选择与否来分隔。
所以特判
a
[
1
]
a[1]
a[1]是否选择,然后从
a
[
2
]
a[2]
a[2]开始
d
p
dp
dp
当选择了
a
[
1
]
a[1]
a[1],那么
a
[
n
]
a[n]
a[n]可选可不选
当没有选择
a
[
1
]
a[1]
a[1],那么
a
[
n
]
a[n]
a[n]必须选择,否则第
1
1
1个元素不会被覆盖
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 300010;
int a[N], n;
ll f[N][2];
void solve() {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
// 选a[1]
f[1][0] = 1e18, f[1][1] = a[1];
for(int i = 2; i <= n; ++i) {
f[i][0] = f[i - 1][1];
f[i][1] = min(f[i - 1][0], f[i - 1][1]) + a[i];
}
ll ans = min(f[n][0], f[n][1]);
// 不选a[1]
f[1][0] = 0, f[1][1] = 1e18;
for(int i = 2; i <= n; ++i) {
f[i][0] = f[i - 1][1];
f[i][1] = min(f[i - 1][0], f[i - 1][1]) + a[i];
}
ans = min(ans, f[n][1]);
printf("%lld\n", ans);
}
int main()
{
int T = 1;
// scanf("%d", &T);
for(int i = 1; i <= T; ++i) solve();
return 0;
}
F - Two Spanning Trees
题意:
给一个
n
n
n点
m
m
m边的无向连通图,无自环重边。
求两个生成树
T
1
T1
T1和
T
2
T2
T2
- T 1 T1 T1满足条件为:如果 T 1 T1 T1是有根树,根为 1 1 1,对于不属于 T 1 T1 T1的边 u − v u-v u−v,存在 u u u是 v v v的祖先或者 v v v是 u u u的祖先
- T 2 T2 T2满足条件为:如果 T 2 T2 T2是有根树,根为 1 1 1,对于 T 2 T2 T2中的任意一条边 u − v u-v u−v,存在 u u u是 v v v的祖先或者 v v v是 u u u的祖先
T
1
T1
T1就是
d
f
s
dfs
dfs,这样不属于
T
1
T1
T1的边必然是返祖边
T
2
T2
T2就是
b
f
s
bfs
bfs,这样所有的边之间有父亲和儿子关系,所以任意一条边中,存在一个点
u
u
u是点
v
v
v的父亲。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200010;
const int M = N << 1;
int h[N], e[M], ne[M], idx;
int n, m;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int vis[N];
void dfs(int u) {
if(vis[u]) return ;
vis[u] = 1;
for(int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if(!vis[v]) {
printf("%d %d\n", u, v);
dfs(v);
}
}
}
void bfs(int s) {
queue<int> q;
q.push(s);
vis[s] = 1;
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if(!vis[v]) {
printf("%d %d\n", u, v);
q.push(v);
vis[v] = 1;
}
}
}
}
void solve(int ca) {
scanf("%d%d", &n, &m);
memset(h, -1, (n + 1) * sizeof(int));
idx = 0;
for(int i = 1; i <= m; ++i) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
add(b, a);
}
dfs(1);
memset(vis, 0, (n + 1) * sizeof(int));
bfs(1);
}
int main()
{
int T = 1;
// scanf("%d", &T);
for(int i = 1; i <= T; ++i) solve(i);
return 0;
}