AtCoder Beginner Contest 222

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 aicibi
思路:
设置状态 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 的数量
状态转移:

  1. 选择第 i i i 位为 i − 1 i - 1 i1 位上的最大数字,但是要保证这个数是小于等于当前位数字 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[i1][min(j,b[i1]]
  2. 选择比上一位数字大 1 1 1 的数
    f [ i ] [ j ] + = f [ i − 1 ] [ j − 1 ] f[i][j] += f[i - 1][j - 1] f[i][j]+=f[i1][j1]
  3. 上述选择的数字范围应当都在 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 
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 n1 条无向边的树,现在要将树上的每条边染成蓝色或者红色,使得沿着给定的路径 A [ i ] A[i] A[i] 走后,经过的红色边的数量 R R R 与蓝色边的数量 B B B 的差值 R − B = K R - B = K RB=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] j2num[i]
所以: f [ j ] + = f [ j − 2 ∗ n u m [ i ] ] f[j] += f[j - 2 * num[i]] f[j]+=f[j2num[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';
}

[F - Expensive Expense(https://atcoder.jp/contests/abc222/tasks/abc222_f)

题目概述:
给定一个 n n n 个城市 n − 1 n - 1 n1 条道路的树,每一个城市都有一个点权,每条路都有边权,现在给定从城市 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';
}

你可能感兴趣的:(刷题,算法,c++,排序算法)