2019中国大学生程序设计竞赛-女生专场 题解

1001.Ticket
签到
1002. Gcd
签到
1003. Function

解法:我们用一个优先队列存fi(x + 1) - fi(x),初始化我们把所有方程的x设置为1,并且全部存进优先队列,最小值优先,每次出队,我们都把自变量调大一个单位继续进队,依次进出队m - n 次,最后取出优先队列所有函数值并求和即可。
#include
#define ll long long
using namespace std;
const int maxn = 1e5 + 10;
ll d[maxn];
int vis[maxn], n, m;
struct node {
     
    int a, b, c, x;
    ll v;
    bool operator<(const node& t) const {
     
        return v > t.v;
    }
};
priority_queue<node> q;
ll gao(int a, int b ,int c, int x) {
     
    return a * x * x + b * x + c;
}
int main() {
     
    int a, b, c;
    cin>>n>>m;
    for (int i = 1; i <= n; i++) {
     
        cin>>a>>b>>c;
        ll v = gao(a, b, c, 2) - gao(a, b, c, 1);
        q.push(node{
     a, b, c, 1, v});
    }
    for (int i = 0; i < m - n; i++) {
     
        node tmp = q.top();
        q.pop();
        tmp.x++;
        tmp.v = gao(tmp.a, tmp.b, tmp.c, tmp.x + 1) - gao(tmp.a, tmp.b, tmp.c, tmp.x);
        q.push(tmp);
    }
    ll ans = 0;
    while (!q.empty()) {
     
        node tmp = q.top();
        ans += gao(tmp.a, tmp.b, tmp.c, tmp.x);
        q.pop();
    }
    cout<<ans<<endl;
}

1004. Tree
裸树链剖分

#include
#define ll long long
using namespace std;
const int maxn = 1e5 + 10;
ll sum[maxn *4];
int mx[maxn * 4], top[maxn], dep[maxn], son[maxn];
int sz[maxn], cnt, id[maxn], f[maxn], n, a[maxn];
vector<int> G[maxn];
void dfs(int u, int fa) {
     
    f[u] = fa;
    dep[u] = dep[fa] + 1;
    sz[u] = 1;
    for (auto v : G[u])
        if (v != fa) {
     
            dfs(v, u);
            sz[u] += sz[v];
            if (sz[v] > sz[son[u]])
                son[u] = v;
        }
}
#define ls o * 2
#define rs o * 2 + 1
void update(int o, int l, int r, int k, int v) {
     
    if (l == r) {
     
        mx[o] = sum[o] = v;
        return;
    }
    int m = (l + r) / 2;
    if (k <= m)
        update(ls, l, m, k, v);
    else
        update(rs, m + 1, r, k, v);
    mx[o] = max(mx[ls], mx[rs]);
    sum[o] = sum[ls] + sum[rs];
}
void dfs2(int u, int rt) {
     
    top[u] = rt;
    id[u] = ++cnt;
    update(1, 1, n, cnt, a[u]);
    if (son[u])
        dfs2(son[u], rt);
    for (auto v : G[u])
        if (v != f[u] && v != son[u])
            dfs2(v, v);
}
void up(int o, int l, int r, int ql, int qr) {
     
    if (mx[o] <= 1)
        return;
    int m = (l + r) / 2;
    if (l >= ql && r <= qr) {
     
        if (l == r) {
     
            sum[o] = mx[o] = sqrt(mx[o]);
            return;
        }
        if (mx[ls] > 1)
            up(ls, l, m, ql, qr);
        if (mx[rs] > 1)
            up(rs, m + 1, r, ql, qr);
        mx[o] = max(mx[ls], mx[rs]);
        sum[o] = sum[ls] + sum[rs];
        return;
    }
    if (ql <= m)
        up(ls, l, m, ql, qr);
    if (qr > m)
        up(rs, m + 1, r, ql, qr);
    mx[o] = max(mx[ls], mx[rs]);
    sum[o] = sum[ls] + sum[rs];
}
ll qu(int o, int l, int r, int ql, int qr) {
     
    if (l >= ql && r <= qr)
        return sum[o];
    int m = (l + r) / 2;
    ll res = 0;
    if (ql <= m)
        res += qu(ls, l, m, ql, qr);
    if (qr > m)
        res += qu(rs, m + 1, r, ql, qr);
    return res;
}
void up_path(int x, int y) {
     
    while (top[x] != top[y]) {
     
        if (dep[top[x]] < dep[top[y]])
            swap(x, y);
        up(1, 1, n, id[top[x]], id[x]);
        x = f[top[x]];
    }
    if (id[x] > id[y])
        swap(x, y);
    up(1, 1, n, id[x], id[y]);
}
ll qu_path(int x, int y) {
     
    ll res = 0;
    while (top[x] != top[y]) {
     
        if (dep[top[x]] < dep[top[y]])
            swap(x, y);
        res += qu(1, 1, n, id[top[x]], id[x]);
        x = f[top[x]];
    }
    if (id[x] > id[y])
        swap(x, y);
    return res + qu(1, 1, n, id[x], id[y]);
}
int main() {
     
    int m, opt, u, v;
    cin>>n>>m;
    for (int i = 1; i <= n; i++)
        cin>>a[i];
    for (int i = 1; i < n; i++) {
     
        cin>>u>>v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1, 0);
    dfs2(1, 1);
    while (m--) {
     
        cin>>opt>>u>>v;
        if (!opt)
            up_path(u, v);
        else
            printf("%lld\n", qu_path(u, v));
    }
}

1005. Checkout

解法:我们用map[u]存u节点所有儿子权值信息,然后进行一次dfs,枚举每个节点u当领导的贡献,首先 u 的原领导 rt 离职了,那么ans -= mp[rt][a[rt]],然后我们要把 u 的儿子和 rt 的儿子合并,枚举 u 所有儿子权值 val,ans += mp[u][val] * mp[rt][val],不过 u 作为 rt 的儿子和 u 的儿子合并多计算了一次贡献,所以ans -= mp[u][a[u]]
update:上述做法没考虑周全,已被评论区数据hack(感谢),如果 rt 还有父亲 root,那么我们还用统计 rt 变成 u 之后对 root 以及 root 的儿子产生的贡献的变化,这个问题很简单,交给你啦
#include
#define ll long long
using namespace std;
const int maxn = 1e5 + 10;
map<int, int> mp[maxn];
int a[maxn], f[maxn];
vector<int> G[maxn];
ll sum, ans[maxn];
void dfs(int u, int fa) {
     
    f[u] = fa;
    for (auto v : G[u])
    if (v != fa) {
     
        dfs(v, u);
        if (a[v] == a[u])
            sum++;
        sum += mp[u][a[v]];
        mp[u][a[v]]++;
    }
}
void dfs2(int u) {
     
    int rt = f[u];
    if (rt) {
     
        ll res = sum - mp[rt][a[rt]];
        for (auto tmp : mp[u])
            res += 1ll * tmp.second * mp[rt][tmp.first];
        res -= mp[u][a[u]];
        int root = f[rt];
        if (root) {
     
            res -= (mp[root][a[rt]] - 1);
            if (a[root] == a[rt])
                res--;
            if (a[u] == a[root])
                res++;
            res += mp[root][a[u]];
        }
        ans[rt] = max(ans[rt], res);
    }
    if (G[u].size() == 1 && u != 1) {
     
        ans[u] = sum - mp[rt][a[u]] + 1;
        if (a[rt] == a[u])
            ans[u]--;
    }
    for (auto v : G[u])
        if (v != f[u])
            dfs2(v);
}
int main() {
     
    int n, m, u, v;
    cin>>n>>m;
    for (int i = 1; i <= n; i++)
        cin>>a[i];
    for (int i = 1; i < n; i++) {
     
        cin>>u>>v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1, 0);
    dfs2(1);
    for (int i = 1; i <= n; i++)
        printf("%lld%c", ans[i], i == n ? '\n' : ' ');
}

1006. String

解法:我们设d[i][j][p]为前 i 个字符,含有 p 段字符串,最后一个字母是 j + ‘a’ 所需要最少的操作数,对于第 i 个字符 c,我可以不修改它,d[i][c][p] = min(d[i][c][p], d[i - 1][c][p]),对于所有 j (j != c),d[i][c][p] = min(d[i][c][p], d[i - 1][j][p - 1]),我们修改它,那么最好是直接整段修改到 pre= max(0, i - L),枚举字符 c,对于所有 j != c,显然d[i][c][p] = min(d[i][c][p], d[pre][j][p - 1] + 1),我们可以预处理出d[pre][j][p - 1] 的前缀最大值,后缀最大值,再进行转移,复杂度 n * 26 * K。//最后答案应该还有枚举段数,之前写瑕疵了,感谢网友的指出
#include
using namespace std;
const int maxn = 1e5 + 10;
int d[maxn][26][11], sum[28], suf[28];
char s[maxn];
void up(int &x, int y) {
     
    x = min(x, y);
}
int main() {
     
    int n, l, k;
    cin>>n>>l>>k>>s + 1;
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < 26; j++)
            for (int p = 0; p <= k; p++)
                d[i][j][p] = 1e9;

    d[1][s[1] - 'a'][1] = 0;
    for  (int i = 0; i < 26; i++)
        up(d[1][i][1], 1);

    for (int i = 2; i <= n; i++)
    for (int p = 1; p <= min(i, k); p++) {
     
        up(d[i][s[i] - 'a'][p], d[i - 1][s[i] - 'a'][p]);
        for (int j = 0; j < 26; j++)
            if (j != s[i] - 'a')
                up(d[i][s[i] - 'a'][p], d[i - 1][j][p - 1]);
        int pre = max(0, i - l);

        suf[25] = d[pre][25][p - 1];
        for (int j = 24; j >= 0; j--)
            suf[j] = min(suf[j + 1], d[pre][j][p - 1]);

        int mn = d[pre][0][p - 1];
        for (int j = 0; j < 26; j++) {
     
            up(d[i][j][p], d[pre][j][p] + 1);
            int t1 = 1e9, t2 = 1e9;
            if (j)
                up(t1, mn);
            if (j != 25)
                up(t2, suf[j + 1]);
            mn = min(mn, d[pre][j][p - 1]);
            up(d[i][j][p], min(t1, t2) + 1);
        }
    }
    int ans = 1e9;
    for (int i = 0; i < 26; i++)
        for (int j = 1; j <= k; j++)
            up(ans, d[n][i][j]);
    cout<<ans<<endl;
}
/*
4 1 4
bbbb
*/

1007. Circle
咕咕咕

1008. Clock
咕咕咕

1009. Union
没读题(应该不会)

1010. Tangram

解法:我们发现,加一条线,最多有5条线相交,那么 ans += 6,再加一条线,就会有6条线与之相交,那么ans += 7,那么很显然,加n条边的总区域数是:7 + (6 + n + 5) * n / 2。
#include
#define ll long long
using namespace std;
int main() {
     
    ll n;
    while (cin>>n) {
     
        ll res = 7 + (11 + n) * n / 2;
        cout<<res << '\n';
    }
}

1011. Tetris
咕咕咕

你可能感兴趣的:(动态规划)