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;
}
原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/14546699.html