zoukankan      html  css  js  c++  java
  • ICPC 2019 Pacific Northwest (Div. 1)

    2019-2020 ACM-ICPC Pacific Northwest Regional Contest (Div. 1)

    A Radio Prize

    大意:

    求出一颗树上每个点到其他点的距离的和,两个点之间的距离定义为两个点的权值和乘上路径权值和

    思路:

    明显是换根DP,不过写的时候写了好久...

    将所求拆成两个数:这个点的权值乘路径权值和+目标点的权值乘路径权值和,这样就可以维护了,换根的时候求出父节点到这个节点的贡献即可

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e6 + 5;
    typedef long long LL;
    #define int LL
    
    int n, a[N], sumv[N];
    int SUMW = 0;
    int sumV[N];
    vector<pair<int, int>> mp[N];
    int dp[N], dis[N][2], sz[N];
    int res[N];
    void dfs1(int now, int fa) {
        sumv[now] = a[now];
        sz[now] = 1;
        for (int i = 0; i < mp[now].size(); i++) {
            int ne = mp[now][i].first, w = mp[now][i].second;
            if (ne == fa) continue;
            dfs1(ne, now);
            sz[now] += sz[ne];                    //子节点数量
            dp[now] += dp[ne] + w * sumv[ne];  //子节点的贡献
            sumv[now] += sumv[ne];                //子节点权值和
            dis[now][0] += dis[ne][0] + w * sz[ne];  //子节点距离和
        }
    }
    
    void dfs2(int now, int fa) {
        for (int i = 0; i < mp[now].size(); i++) {
            int ne = mp[now][i].first, w = mp[now][i].second;
            if (ne == fa) continue;
    
            dis[ne][1] = dis[now][1] - w * sz[ne] + w * (n - sz[ne]);
            res[ne] = dp[ne] + dis[ne][1] * a[ne] + res[now] -
                      dis[now][1] * a[now] - (dp[ne] + w * sumv[ne]) +
                      w * (SUMW - sumv[ne]);
            dfs2(ne, now);
        }
    }
    
    signed main() {
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i],SUMW += a[i];
        for (int i = 1; i <= n - 1; i++) {
            int x, y, w;
            cin >> x >> y >> w;
            
            mp[x].push_back({y, w}), mp[y].push_back({x, w});
        }
        dfs1(1, 0);
        res[1] = dp[1] + dis[1][0] * a[1];
        dis[1][1] = dis[1][0];
        dfs2(1, 0);
        for (int i = 1; i <= n; i++) cout << res[i] << endl;
        return 0;
    }
    

    B Perfect Flush

    大意:

    给出n个数,n个数都是由1到k的数组成,现在要从里面找到一个字典序最小的1到k的全排列

    思路:

    用栈去维护,如果当前栈顶的数字在后面还会出现,且比当前要入栈的数字大,那么就弹栈

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long LL;
    int const MAXN = 2e5 + 10;
    int n, k;
    int a[MAXN];
    int pos[MAXN];
    stack<int> sta;
    int vis[MAXN];
    vector<int> ans;
    int main() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        cin >> n >> k;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            pos[a[i]] = i;
        }
        for (int i = 1; i <= n; i++) {
            if (vis[a[i]]) continue;
            while (sta.size()) {
                int t = sta.top();
                if (a[i] >= t) break;
                if (pos[t] < i) break;
                vis[t] = 0;
                sta.pop();
            }
            sta.push(a[i]);
            vis[a[i]] = 1;
        }
        while (sta.size()) {
            //cout << sta.top() << " ";
            ans.push_back(sta.top());
            sta.pop();
        }
        for (int i = ans.size() - 1; i >= 0; i--) {
            cout << ans[i] << " ";
        }
        return 0;
    }
    

    C Coloring Contention

    大意:

    给出一个无向图,Bob要从1号点走到n号点,Alice现在要给每条边染色,他可以染红色或者蓝色,Alice想让Bob经过的路径中颜色变换次数最多,Bob则想让自己经过的颜色变换次数最少,问Alice能让Bob经过的颜色变换次数最多是多少次

    思路:

    直接bfs,求出1到n的最短路,然后最短路径-1即可

    #include <bits/stdc++.h>
    
    #define int long long
    #define debug(x) cout << #x << " = " << x << endl;
    using namespace std;
    
    int const MAXN = 2e5 + 10, MAXM = MAXN * 2;
    int n, m, T, e[MAXN], ne[MAXM], h[MAXN], idx, st[MAXN], dis[MAXN];
    queue<int> q;
    
    void add(int a, int b) {
        e[idx] = b, ne[idx] = h[a], h[a] = idx++;
    }
    
    void bfs(int S) {
        q.push(S);
        st[S] = 1;
        while(q.size()) {
            auto t = q.front();
            q.pop();
    
            for (int i = h[t]; ~i; i = ne[i]) {
                int j = e[i];
                if (st[j]) continue;
                st[j] = 1;
                dis[j] = dis[t] + 1;
                q.push(j);
            }
        }
    }
    
    signed main() {
        memset(h, -1, sizeof h);
        cin >> n >> m;
        for (int i = 1, a, b; i <= m; ++i) {
            cin >> a >> b;
            add(a, b), add(b, a);
        }
        bfs(1);
        cout << max((long long)0, dis[n] - 1);
        return 0;
    }
    

    D Dividing by Two

    大意:

    给出两个点A和B,现在每次操作可以将A除以2(此时A必须为偶数),或者将A加1,问最少多少次操作可以将A变换为B

    思路:

    当A小于B时,肯定只能不停+1

    当A大于B时,首先将其变换到B到2*B 的区间,然后判断先除后减还是先减后除

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e6 + 5;
    typedef long long LL;
    #define int LL
    int a, b;
    signed main() {
        cin >> a >> b;
        if (a <= b) {
            cout << b - a << endl;
        } else {
            int res = 0;
            swap(a, b);
            while (1) {
                if (b >= a && b <= 2 * a) {
                    int tmp;
                    if (b % 2 == 0)
                        tmp = min(2 * a - b + 1, 1 + a - b / 2);
                    else
                        tmp = min(2 * a - b + 1, 2 + a - (b + 1) / 2);
                    cout << res + tmp << endl;
                    return 0;
                }
                if(b%2==0){
                    res++;
                    b /= 2;
                }
                else{
                    res++;
                    b++;
                }
            }
        }
        return 0;
    }
    

    E Rainbow Strings

    大意:

    给定一个长度为n字符串,要求从中选择一些子序列,使得子序列中每种字符只出现一次,问这样的子序列有多少个?

    思路:

    可以计算一下原来的字符串中每个字符出现的次数,那么子序列就是每次从不同的字符串挑选出一个或者不选的方案,即(c[0] + 1) * (c[1] + 1) * (c[2] + 1) * ... * (c[n] + 1)

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define int long long
    int const MAXN = 2e5 + 10;
    int const mod = 11092019;
    int n, m, T;
    int num[MAXN];
    signed main() {
        string s;
        cin >> s;
        for (int i = 0; i < s.size(); i++) {
            num[s[i] - 'a']++;
        }
        int ans = 1;
        for (int i = 0; i < 26; i++) {
            ans = ans * (num[i] + 1) % mod;
        }
        cout << ans;
        return 0;
    }
    

    I Error Correction

    大意: 给定n个字符串,每个字符串中不存在重复出现的字符。现在要从这n个字符串中选择一个子集,使得子集中不存在两个字符串仅仅交换2个字符就会完全相同的情况。问这样的子集最大是多大。(1<=n<=500)

    思路:

    最大独立集。对于每个字符串看作一个点,如果2个字符串通过交换2个字符会相同,那么连一条边,那么就是找到一个最大的独立集。那么答案就是n - 最大匹配数目。

    #include <bits/stdc++.h>
    
    #define int long long
    #define debug(x) cout << #x << " = " << x << endl;
    using namespace std;
    
    int const MAXN = 5e2 + 10;
    int n, m, T, g[MAXN][MAXN], st[MAXN], match[MAXN];
    string s[1000];
    
    int check(int x, int y) {
        string s1 = s[x], s2 = s[y];
        int len = s1.size();
        int cnt = 0;
        for (int i = 0; i < len; ++i) {
            if (s1[i] != s2[i]) cnt++;
        }
        return cnt == 2;
    }
    
    bool find(int x) {
        for (int i = 1; i <= n; ++i) {
            if (!g[x][i]) continue;
            if (!st[i]) {
                st[i] = 1;
                if (!match[i] || find(match[i])) {
                    match[i] = x;
                    return true;
                }
            }
        }
        return false;
    }
    
    int hungary() {
        int ans = 0;
        for (int i = 1; i <= n; ++i) {
            memset(st, 0, sizeof st);
            if (find(i)) ans++;
        }
        return ans;
    }
    
    signed main() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        cin >> n;
        for (int i = 1; i <= n; ++i) cin >> s[i];
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (i != j && check(i, j)) g[i][j] = g[j][i] = 1;
            }
        }
    
        cout << n - hungary() / 2;
    
        return 0;
    }
    

    J Interstellar Travel

    大意:

    给出n个点,每个点都有三个属性T S A,每个点的贡献(max(0,Ti−si·dist(ai,a)))

    a为飞船飞行的角度,所以对于每个飞行角度,n个点的总贡献T是一定的,现在要求求出最大的T

    思路:

    据说正解是求出函数关系式,不过写了一发模拟退火+卡时过了...

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e6 + 5;
    typedef long long LL;
    int n;
    long double t[N], s[N], a[N];
    const long double eps = 1e-8L;
    const long double pi = acos(-1.0L);
    int sgn(long double x) {
        if (fabs(x) < eps) return 0;
        if (x < 0)
            return -1;
        else
            return 1;
    }
    long double res = 0;
    long double getsum(long double x) {
        long double re = 0;
        for (int i = 0; i < n; i++) {
            long double dis = fabs(x - a[i]);
            if (sgn(dis-pi) >= 0) dis=2.0L*pi-dis;
            re += max(0.0L, t[i] - s[i] * dis);
        }
        res = max(res, re);
        return re;
    }
    
    long double rand(long double l, long double r) {
        return (long double)rand() / RAND_MAX * (r - l) + l;
    };
    
    void sa() {
        long double p = rand(0.0L, 2.0L * pi);
        for (long double t = 2.0L * pi; t > 1e-8L; t *= 0.99L) {
            long double np = rand(max(0.0L,p - t), min(2.0L*pi,p + t));
            long double dt = getsum(np) - getsum(p);
            if (exp(dt / t) > rand(0, 1)) {
                p = np;
            }
        }
    }
    
    int main() {
        cin >> n;
        srand((unsigned)time(NULL));
        for (int i = 0; i < n; i++) {
            cin >> t[i] >> s[i] >> a[i];
        }
        while ((double)clock() / CLOCKS_PER_SEC < 3.0) sa();
        printf("%0.7Lf
    ", res);
        return 0;
    }
    

    L Carry Cam Failure

    大意:

    定义不进位计算为无论对于加法还是乘法,所有的进位全部不计算。现在给你N,要求判断是否存在a,使得a * a = N。如果不存在,那么打印-1

    思路:

    模拟题。直接模拟每一位出现的情况,然后剪枝。每次的情况最多为2,复杂度为:(O(2^{n/2}))

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long LL;
    int const MAXN = 30;
    int n, m, T;
    string s;
    int flag;
    int pos;
    int bit[MAXN];
    int need[MAXN];
    int sum[MAXN];
    bool check(int k) {
        if (k == pos - 1 && bit[k] * bit[k] % 10 == need[k * 2]) return 1;
        if (k != pos - 1 && (sum[k + pos - 1] + bit[k] * bit[pos - 1] * 2) % 10 ==
                                need[k + pos - 1])
            return 1;
        return 0;
    }
    void dfs(int k) {
        // 搜索到头,扫一遍与输入比较
        if (k == -1) {
            flag = 1;
            for (int i = pos - 1; i >= 0; i--) {
                if (sum[i] % 10 != need[i]) {
                    flag = 0;
                    return;
                }
            }
            return;
        }
        if (flag) return;
        for (int i = 0; i < 10; i++) {
            if (flag) return;
            bit[k] = i;
            if (check(k)) {
                for (int j = pos - 1; j > k; j--)
                    sum[j + k] += 2 * (bit[k] * bit[j]) % 10;
                sum[2 * k] += bit[k] * bit[k] % 10;
                dfs(k - 1);
                if (flag) return;
                for (int j = pos - 1; j > k; j--)
                    sum[j + k] -= 2 * (bit[k] * bit[j]) % 10;
                sum[2 * k] -= bit[k] * bit[k] % 10;
            }
        }
    }
    
    int main() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        cin >> s;
        int len = s.size();
        if (len % 2 == 0) return puts("-1"), 0;
        for (int i = 0; i < len; i++) need[i] = s[len - i - 1] - '0';
        pos = (len + 1) / 2;
        dfs(pos - 1);
        if (flag) {
            for (int i = pos - 1; i >= 0; i--) cout << bit[i];
        } else
            puts("-1");
        return 0;
    }
    
    

    M Maze Connect

    大意:

    给定一个迷宫网格,每个字符为"", "/", "."。问你这个迷宫网格中有多少个简单环。

    样例解释如下:

    4 4
    /..
    ..
    ./
    ../
    

    这样是2个环

    思路:

    dfs找简单环,每次走过就打个标记,遇到打过标记的点就说明找到了环。然后每次dfs找到的环除以2就是找到的环数目

    #include <bits/stdc++.h>
    
    #define int long long
    using namespace std;
    
    int const MAXN = 1e3 + 10, pMAXN = MAXN * MAXN, pMAXM = pMAXN * 4 * 2;
    int n, m, T, e[pMAXM], ne[pMAXM], h[pMAXN], idx, vis[pMAXN], res, tmp;
    char s[MAXN][MAXN];
    
    int get(int x, int y) {
        return x * (m + 1) + y;
    }
    
    void add(int a, int b) {
        e[idx] = b, ne[idx] = h[a], h[a] = idx++;
    }
    
    void dfs(int u, int fa) {
        for (int i = h[u]; ~i; i = ne[i]) {
            int j = e[i];
            if (j == fa) continue;
            if (vis[j] == 1) {
                tmp ++;
                continue;
            }
            vis[j] = 1;
            dfs(j, u);
        }
        return;
    }
    
    signed main() {
        cin >> n >> m;
        memset(h, -1, sizeof h);
        for (int i = 0; i < n; ++i) scanf("%s", s[i]);
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (s[i][j] == '/') add(get(i + 1, j), get(i, j + 1)), add(get(i, j + 1), get(i + 1, j));
                if (s[i][j] == '\') add(get(i, j), get(i + 1, j + 1)), add(get(i + 1, j + 1), get(i, j));
            }   
        }
    
        for (int i = 0; i <= n; ++i) {
            for (int j = 0; j <= m; ++j) {
                if (vis[get(i, j)]) continue;
                vis[get(i, j)] = 1;
                tmp = 0;
                dfs(get(i, j), -1);
                res += tmp / 2;
            }
        }
        cout << res;
    
        return 0;
    }
    
  • 相关阅读:
    象棋
    支持向量机
    朴素贝叶斯分类
    规则归纳
    监督学习
    序列模式
    关联规则
    从《数据挖掘概念与技术》到《Web数据挖掘》
    数据挖掘在大数据中的应用综述
    join sql图
  • 原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/14546699.html
Copyright © 2011-2022 走看看