abc209

C - Not Equal 285

求长度为 n,两两不同,且满足 1aici 的数组的数量

数组 c 排序,答案就是 i(ci(i1)),其中 i1 个位置被前面占了

D - Collision 686

给定一棵树,q 次询问,一个人从 u 走向 v,另一个人从 v 走向 u,同时同速。问两人在点上还是在边上相遇

若 u 和 v 深度奇偶性相同则在点上相遇,否则在边上

E - Shiritori 2153

两个人成语接龙,每个人说的单词前三个字母须与上一个人说的最后三个字母同。给定单词表,单词可重复使用,不能说者输,问从每个单词出发谁会赢(或者平局)

把每个单词视为边,把边(单词)的状态放在它的终端点(三个字母)上,按拓扑排序的逆序递推状态,最后没有状态的点是平局

(似乎我对输赢的定义跟大家相反)

先手必败点:存在先手必胜后继点

先手必胜点:所有后继点必败

注意:跟正常拓扑排序不同,不是度为0才入队,而是状态确定就入队,度只是为状态的推断服务的。否则,若 u 的后继既有必胜点又有个环,可能导致 u 的状态没法更新,但实际上 u 是必败点

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    int n;
    cin >> n;
    vector<string> edges;
    map<string, vector<string>> g;
    map<string, int> deg;
    while (n--) {
        string s;
        cin >> s;
        string u = s.substr(0, 3), v = s.substr(s.size() - 3); // u -> v
        edges.push_back(v);
        g[v].push_back(u); // 反图
        deg[u]++;
        if (!deg.count(v)) deg[v] = 0; // 别漏了
    }
    
    queue<string> q;
    map<string, int> f;
    for (auto [s, d] : deg) {
        if (!d) q.push(s), f[s] = 1; // 先手胜
    }
    while (q.size()) {
        auto v = q.front(); q.pop();
        for (auto u : g[v]) {
            deg[u]--;
            if (f[v] == 1 && f[u] != 2) f[u] = 2, q.push(u); //首次确定u为必败态
            else if (!deg[u] && f[u] != 2) f[u] = 1, q.push(u); //拆完出边仍未必败的才是必胜态
        }
    }
    
    string ans[] = {"Draw", "Takahashi", "Aoki"};
    for (auto s : edges) {
        cout << ans[f[s]] << '\n';
    }
    
    return 0;
}

F - Deforestation 2307

把 n 棵树砍完,每次选择一棵树 i,花费 ai1+ai+ai+1 然后使 ai 变成 0。问有多少种砍树顺序的花费并列最小

对于相邻两项 ai1,ai,应该先砍较高的。不相邻的两棵树实际上不会相互影响

f(i,j) 表示砍前 i 棵树,第 i 棵树是第 j 个被砍的。注意 j 不是最终顺序

ai<ai1,则 f(i,j)=f(i1,1)++f(i1,j1),注意这里实际上是通过把第 j,j+1, 个被砍的树后移以腾出 j 号位置,然后把 ai 放在 j 处来得到新的砍树顺序的

ai>ai1,则 f(i,j)=f(i1,j)+f(i1,j+1)++f(i1,i1),注意根据上述构造方法, f(i1,j) 也要算入

ai=ai1,那么可以把 ai 插入前 i1 棵树的任何砍树顺序的任何位置

记录一下前缀和,O(n2)。题解把这类 dp 叫 “insertion DP”

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll P = 1e9 + 7;
const int N = 4e3 + 5;

ll n, a[N], f[N][N], s[N];

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    
    f[1][1] = s[1] = 1;
    for (int i = 2; i <= n; i++) {
        for (int j = 1; j <= i; j++) {
            if (a[i] < a[i - 1]) f[i][j] = s[j - 1];
            if (a[i] > a[i - 1]) f[i][j] = (s[i - 1] - s[j - 1]) % P;
            if (a[i] == a[i - 1]) f[i][j] = s[i - 1];          
        }
        for (int j = 1; j <= i; j++)
            s[j] = (s[j - 1] + f[i][j]) % P;
    }
    
    ll ans = accumulate(f[n] + 1, f[n] + 1 + n, 0ll) % P;
    cout << (ans + P) % P;
    
    return 0;
}

posted @   Bellala  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示