题目概述:
给定两个单调不下降的序列 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 的数量
状态转移:
初始化:
f [ 0 ] [ 0 ] = 1 f[0][0] = 1 f[0][0]=1
代码:
#include
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';
}
题目概述:
给定一棵有 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
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';
}
题目概述:
给定一个 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
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';
}