D - Between Two Arrays
题目概述:
给定两个单调不下降的序列
a
i
a_i
ai 和
b
i
b_i
bi, 求 一个单调不下降的序列
c
i
c_i
ci 的个数, 满足
a
i
≤
c
i
≤
b
i
a_i \le c_i \le b_i
ai≤ci≤bi
思路:
设置状态
f
[
i
]
[
j
]
f[i][j]
f[i][j] 表示
c
i
c_i
ci 的第
i
i
i 位数,并且当前最后一位不超过
j
j
j 的序列
c
i
c_i
ci 的数量
状态转移:
- 选择第
i
i
i 位为
i
−
1
i - 1
i−1 位上的最大数字,但是要保证这个数是小于等于当前位数字
j
j
j 的
f [ i ] [ j ] + = f [ i − 1 ] [ m i n ( j , b [ i − 1 ] ] f[i][j] += f[i - 1][min(j, b[i - 1]] f[i][j]+=f[i−1][min(j,b[i−1]] - 选择比上一位数字大
1
1
1 的数
f [ i ] [ j ] + = f [ i − 1 ] [ j − 1 ] f[i][j] += f[i - 1][j - 1] f[i][j]+=f[i−1][j−1] - 上述选择的数字范围应当都在 a [ i ] a[i] a[i] ~ b [ i ] b[i] b[i] 之间
初始化:
f
[
0
]
[
0
]
=
1
f[0][0] = 1
f[0][0]=1
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<int, int> PII;
const int N = 3010, mod = 998244353;
int n;
int f[N][N];
int a[N], b[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
freopen("D:/Cpp/program/Test.in", "r", stdin);
freopen("D:/Cpp/program/Test.out", "w", stdout);
#endif
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
for(int i = 1; i <= n; i ++) cin >> b[i];
f[0][0] = 1;
for(int i = 1; i <= n; i ++) {
for(int j = a[i]; j <= b[i]; j ++) {
f[i][j] += f[i - 1][min(j, b[i - 1])];
f[i][j] += f[i][j - 1];
f[i][j] %= mod;
}
}
cout << f[n][b[n]] << '\n';
}
E - Red and Blue Tree
题目概述:
给定一棵有
n
n
n 个顶点
n
−
1
n - 1
n−1 条无向边的树,现在要将树上的每条边染成蓝色或者红色,使得沿着给定的路径
A
[
i
]
A[i]
A[i] 走后,经过的红色边的数量
R
R
R 与蓝色边的数量
B
B
B 的差值
R
−
B
=
K
R - B = K
R−B=K,问一共有多少种染色方案。
思路:
首先需要处理出沿着给定路径走后,会经过每条边的次数,因为是沿着最短路走,所以可以用一次
B
F
S
(
)
BFS()
BFS() 预处理出来。
然后问题就可以变成:有
x
x
x 个大小不同的物体,每个物体的质量为
n
u
m
[
x
]
num[x]
num[x],现在要将物体分为两组,使得一组的质量减去另外一组的质量之后的值为
K
K
K
我们设
f
[
k
]
f[k]
f[k] 为两组物品质量之差为
x
x
x 时的方案数。
初始化:
一开始将所有物品归位一组,
f
[
s
u
m
]
=
1
f[sum] = 1
f[sum]=1 (sum 为总物品的质量)
状态转移:
将第
i
i
i 组物品换组时,差值会变为 (假设当前差值为
j
j
j )
j
−
2
∗
n
u
m
[
i
]
j - 2 * num[i]
j−2∗num[i]
所以:
f
[
j
]
+
=
f
[
j
−
2
∗
n
u
m
[
i
]
]
f[j] += f[j - 2 * num[i]]
f[j]+=f[j−2∗num[i]]
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<int, int> PII;
const int N = 1010, M = N * 2, mod = 998244353, K = 1e5 + 10;
int e[M], ne[M], h[N], idx;
int a[N], num[N], f[K];
int n, m, k;
bool st[N];
map<PII, int> mp;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
bool dfs (int u, int fa, int final) {
if(u == final) return true;
for(int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if(j == fa) continue;
if(dfs(j, u, final)) {
num[mp[{u, j}]] ++;
return true;
}
}
return false;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
freopen("D:/Cpp/program/Test.in", "r", stdin);
freopen("D:/Cpp/program/Test.out", "w", stdout);
#endif
cin >> n >> m >> k;
k = abs(k);
memset(h, -1, sizeof h);
for(int i = 1; i <= m; i ++) cin >> a[i];
for(int i = 1; i < n; i ++) {
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
mp[{a, b}] = mp[{b, a}] = i;
}
// cout << id << '\n';
for(int i = 1; i < m; i ++) {
dfs(a[i], -1, a[i + 1]);
// bfs(a[i], a[i + 1]);
}
int sum = 0;
for(int i = 1; i < n; i ++) sum += num[i];
// cout << sum << '\n';
f[sum] = 1;
for(int i = 1; i < n; i ++) {
for(int j = 0; j <= sum; j ++) {
f[j] += f[j + 2 * num[i]];
f[j] %= mod;
}
}
cout << f[k] << '\n';
}
[F - Expensive Expense(https://atcoder.jp/contests/abc222/tasks/abc222_f)
题目概述:
给定一个
n
n
n 个城市
n
−
1
n - 1
n−1 条道路的树,每一个城市都有一个点权,每条路都有边权,现在给定从城市
i
i
i 到城市
j
j
j 旅游的的花费:从点
i
i
i 到点
j
j
j 的边权 + 点
j
j
j 的点权。现在要求图中每一点
i
i
i 旅游一次的最大花费。
思路:
个人认为该题十分巧妙的地方在于将点权转换为边权,对于点
i
i
i 建一条
n
+
i
n + i
n+i 到
i
i
i 的边,边权为该点的点权。
这样就可以把问题变化为求树的直径的问题。最终答案即是直径的两个端点到图中每一个点的最大值。可以很容易知道端点一定是以上述方式新建出来的点。
只需通过三次
d
f
s
dfs
dfs 找到端点并求出图中某点到两个端点的最大值即可,需要注意一下的是不能到自己所在的城市旅游,最后需要判断一下即可。
具体看代码应该很清楚。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10, M = N * 2;
#define int long long
using ll = long long;
typedef pair<int, int> PII;
int n;
int e[M], ne[M], w[M], h[N], idx;
int p, q;
int dist[N], dist1[N], dist2[N];
ll ans[N];
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}
void dfs(int u, int fa, int distx[]) {
for(int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
// cout << u << '\n';
if(j == fa || distx[j]) continue;
distx[j] = distx[u] + w[i];
// cout << distx[j] << '\n';
dfs(j, u, distx);
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
freopen("D:/Cpp/program/Test.in", "r", stdin);
freopen("D:/Cpp/program/Test.out", "w", stdout);
#endif
cin >> n;
memset(h, -1, sizeof h);
for(int i = 1; i < n; i ++) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
for(int i = n + 1; i <= n * 2; i ++) {
int x;
cin >> x;
// cout << i << ' ' << i - n << ' ';
add(i, i - n, x), add(i - n, i, x);
}
dfs(1, -1, dist);
p = max_element(dist + 1, dist + n * 2 + 1) - dist;
dfs(p, -1, dist1); //距端点 p的最大值
q = max_element(dist1 + 1, dist1 + n * 2 + 1) - dist1;
dfs(q, -1, dist2); //距端点 q的最大值
// cout << p << ' ' << q << '\n';
for(int i = 1; i <= n ; i ++) {
// cout << dist1[i] << ' ' << dist2[i] << '\n';
//不能自己到自己旅游
if(i != p - n && i != q - n) ans[i] = max(dist1[i], dist2[i]);
else if(i == p - n) ans[i] = dist2[i];
else if(i == q - n) ans[i] = dist1[i];
}
for(int i = 1; i <= n; i ++) cout << ans[i] << '\n';
}