Codeforces Round 510 Div.2 解题报告

A. Benches

Description

长凳上已经坐了一些人, 又要来一些人, 问所有长凳上最多的人的最大可能和最小可能.

Solution

堆+贪心乱搞一通.

//maki
#include<bits/stdc++.h>

using namespace std;

const int maxn = 105;

int n, a[maxn], m;
priority_queue<int, vector<int>, greater<int> >q;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    cin >> n >> m;
    for(int i = 1; i <= n; ++i) cin >> a[i], q.push(a[i]);
    for(int i = 1; i <= m; ++i)
    {
        int u = q.top(); q.pop();
        u++;
        q.push(u);
    }
    int mx = 0;
    while(!q.empty()) mx = max(mx, q.top()), q.pop();
    cout << mx << " ";
    sort(a + 1, a + 1 + n);
    cout << a[n] + m << endl;
    return 0;
}

B. Vitamins

Description

每种商品含若干种维他命, 问获取所有维他命的最小花费, 种类 \(\le 3\).

Solution

要是种类很多可以DP吧? 不过3嘛, 嘿嘿嘿.

#include <bits/stdc++.h>

using namespace std;

const int maxn = 1005;

int n, co[maxn];
int a, b, c, ab, bc, ac, abc;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    string s;

    a = b = c = 400005;
    ac = bc = ab = abc = 400005;
    
    cin >> n;
    for(int i = 1; i <= n; ++i)
    {
        cin >> co[i] >> s;
        if(s.size() == 1)
        {
            if(s[0] == 'A')
                a = min(a, co[i]);
            else if(s[0] == 'B')
                b = min(b, co[i]);
            else c = min(c, co[i]);
        }
        else if(s.size() == 2)
        {
            int has[3] = {0,0,0};
            has[s[0]-'A'] = 1;
            has[s[1]-'A'] = 1;
            if(has[0] && has[1]) ab = min(ab, co[i]);
            else if(has[1] && has[2]) bc = min(bc, co[i]);
            else ac = min(ac, co[i]);
        }
        else
        {
            abc = min(abc, co[i]);
        }
    }
    int ans = 400005;
    ans = min(ans, min(a + b + c, abc));
    ans = min(ans, min(ab + bc, ab + ac));
    ans = min(ans, ac + bc);
    ans = min(ans, min(ab + c, bc + a));
    ans = min(ans, ac + b);
    if(ans < 400005) cout << ans << endl;
    else puts("-1");
    return 0;
}

C. Array Product

Description

有两种操作, 第一种是把两个数乘起来放到一个位置, 另一个删掉, 第二种是删掉一个数. 删除后下标不变, 但不能使用. 最大化剩下的一个数的结果.

Solution

有正有负, 肯定就看负数能不能乘出一个整数嘛, 不能就和0合并一下然后删掉.

能的话, 就把0合并一下, 然后删掉.

剩下的无脑乘到第一个没有被删除的位置上就好了.

//maki
#include <bits/stdc++.h>

using namespace std;

const int maxn = 2e+5 + 5;

vector<int> zrs;
int n, a[maxn], negabs = 0x3f3f3f3f, negpos, negcnt;
int vis[maxn];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    cin >> n;
    for(int i = 1; i <= n; ++i)
    {
        cin >> a[i];
        if(a[i] < 0)
        {
            if(abs(a[i]) < negabs) negabs = -a[i], negpos = i;
            negcnt ^= 1;
        }
        if(a[i] == 0) zrs.push_back(i);
    }
    if(!negcnt)
    {
        if(zrs.size())
        {
            for(int i = 1; i < zrs.size(); ++i)
                printf("1 %d %d\n", zrs[i], zrs[0]), vis[zrs[i]] = 1;
            if((int)zrs.size() == n) return 0;
            printf("2 %d\n", zrs[0]); vis[zrs[0]] = 1;
        }
        for(int i = 1; i <= n; ++i)
        {
            if(!vis[i])
            {
                for(int j = i + 1; j <= n; ++j)
                    if(!vis[j]) printf("1 %d %d\n", j, i);
                return 0;
            }
        }
    }
    else
    {
        if(zrs.size())
            for(int i = 0; i < zrs.size(); ++i)
                printf("1 %d %d\n", zrs[i], negpos), vis[zrs[i]] = 1;
        if((int)zrs.size() == n - 1) return 0;
        printf("2 %d\n", negpos); vis[negpos] = 1;
        
        for(int i = 1; i <= n; ++i)
        {
            if(!vis[i])
            {
                for(int j = i + 1; j <= n; ++j)
                    if(!vis[j]) printf("1 %d %d\n", j, i);
                return 0;
            }
        }
    }
    return 0;
}

D. Petya and Array

Description

给一个数组, 求所有和小于 \(t\) 的子区间个数.

Solution

如果都是非负数的话, 双指针就好了, 但是有负数.

一个经典思路然而我并没想到, 好像还讲过, 分治, 每次只考虑跨越当前区间中点的答案, 这样在左边维护后缀和, 右边维护前缀和, 排序以后, 是可以双指针的.

//maki
#include <bits/stdc++.h>

using namespace std;

const int maxn = 2e+5 + 5;

typedef long long ll;

int a[maxn], n;
ll pre[maxn], suf[maxn];
ll ans, t;

void DaC(int l, int r)
{
    if(l == r)
    {
        if(a[l] < t) ans++;
        return;
    }
    //cout << l << " " << r << endl;
    int mid = (l + r) >> 1;
    vector<ll> suf, pre; ll sum = 0;
    for(int i = mid; i >= l; --i)
        sum += a[i], suf.push_back(sum);
    sum = 0;
    for(int i = mid + 1; i <= r; ++i)
        sum += a[i], pre.push_back(sum);
    int llen = suf.size(), rlen = pre.size();
    sort(suf.begin(), suf.end());
    sort(pre.begin(), pre.end());
    int j = rlen;
    //cout << "--" << endl;
    for(int i = 1; i <= llen; ++i)
    {
        while(j && suf[i - 1] + pre[j - 1] >= t) --j;
        //cout << i << " " << j << endl;
        ans += j;
    }
    DaC(l, mid); DaC(mid + 1, r);
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    cin >> n >> t;
    for(int i = 1; i <= n; ++i) cin >> a[i];

    DaC(1, n);
    cout << ans << endl;
    return 0;
}

E. Vasya and Magic Matrix

Description

给一个矩阵, 每个点有值, 每次操作只能从当前的跳到更小值的格子去, 得分是欧几里得距离的平方. 求得分期望.

Solution

先列式子嘛.
\[ E_i = \frac{\sum_{j = 1}^{i - 1}{(x_i - x_j)^2 + (y_i - y_j)^2}}{i - 1} + \sum E \]

然后是转移.

先把式子打开.
\[ E_i =\\ \frac{1}{i-1}\times ((i-1)(x_i^2 + y_i^2) + \sum x_j^2 + \sum y_j^2 - 2\sum{x_ix_j} - 2\sum{y_iy_j} ) + \sum E \]
这样增量的时候, 把该减的减掉该加的加上就可以增量了, 维护一些前缀和, 前缀平方和, 前缀期望和之类的.

记得相等的值要同时做.

//maki
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int maxn = 1005;
const ll p = 998244353;

struct node
{
    ll x, y;
    ll v;
    bool operator < (const node &rhs) const
        {
            return v < rhs.v;
        }
};
int n, m;
ll sumx, sumy, sumxs, sumys, sumf;
ll r, c, f[maxn][maxn], mtr[maxn][maxn];
vector<node> vec;

inline ll quick_power(ll base, ll index)
{
    ll ret = 1;
    while(index)
    {
        if(index & 1) ret = (ret * base) % p;
        index >>= 1;
        base = base * base % p;
    }
    return ret;
}
inline void add(ll &res, ll num)
{
    res += num;
    while(res >= p) res -= p;
    while(res < 0) res += p;
}
inline ll prod(ll a, ll b)
{
    return a * b % p;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    cin >> n >> m;
    for(int i = 0; i < n; ++i)
        for(int j = 0; j < m; ++j)
        {
            cin >> mtr[i][j];
        }
    cin >> r >> c;
    r--; c--;

    for(int i = 0; i < n; ++i)
        for(int j = 0; j < m; ++j)
            vec.push_back((node){i, j, mtr[i][j]});

    sort(vec.begin(), vec.end());

    int i = 0;
    while(i < (int)vec.size())
    {
        int j = i;
        while(j < (int)vec.size() && vec[j].v == vec[i].v) ++j;

        ll inv = -1;
        if(i) inv = quick_power(i, p - 2);
        for(int k = i; k < j; ++k)
        {
            ll xx = vec[k].x, yy = vec[k].y;
            if(inv == -1)
            {
                f[xx][yy] = 0;
                continue;
            }
            add(f[xx][yy], prod(sumf, inv));
            add(f[xx][yy], prod(xx, xx));
            add(f[xx][yy], prod(yy, yy));
            add(f[xx][yy], prod(sumxs, inv));
            add(f[xx][yy], prod(sumys, inv));
            add(f[xx][yy], prod(prod(-xx * 2ll, sumx), inv));
            add(f[xx][yy], prod(prod(-yy * 2ll, sumy), inv));

        }
        for(int k = i; k < j; ++k)
        {
            ll xx = vec[k].x, yy = vec[k].y;
            add(sumf, f[xx][yy]);
            add(sumxs, prod(xx, xx));
            add(sumys, prod(yy, yy));
            add(sumx, xx);
            add(sumy, yy);
        }
        i = j;
    }

    cout << f[r][c] << endl;
    return 0;
}

F. Leaf Sets

Description

把一颗树的叶子节点划分成若干集合, 使得每个集合内最远点对的距离小于等于 \(k\). 求最小集合数.

Solution

本来想二分答案, 比如按深度排个序然后顺次验证什么的, 但是不太可做.

有一个性质, 就是在当前节点 \(v\), 记她到某个孩子的子树内最深节点的距离为 \(d\).

这样就有了若干个 \(d\), 当然是要合并了, 这样才能减少集合, 考虑合并的条件, 当且仅当\(d_a + d_b \le k\), 合并后的集合, 应该有: \(d_u = max(d_a, d_b)\).

再考虑不能合并, 如果有 \(d_a + d_b > k\), 且\(d_a \le d_b\). 那么\(b\)接下来一定没有用了, 因为能和她合并的就能和 \(a\) 合并. 而她又不能再合并, 所以弹掉, 答案+1.

最后做到根节点会剩一些一定合并的集合, 记得答案+1.

//maki
#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e+6 + 5;

struct edge
{
    int to, nxt;
}e[maxn << 1];
int ptr, lnk[maxn], dep[maxn], ans, dgr[maxn], rt, n, k;
set<int> st;

inline void add(int bgn, int end)
{
    e[++ptr] = (edge){end, lnk[bgn]};
    lnk[bgn] = ptr;
}
int dfs(int x, int fa)
{
    if(dgr[x] == 1) return 0;
    vector<int> s;
    for(int p = lnk[x]; p; p = e[p].nxt)
    {
        int y = e[p].to;
        if(y == fa) continue;
        int tmp = dfs(y, x);
        s.push_back(tmp + 1);
    }
    //cout << "dealing" << x << endl;
    sort(s.begin(), s.end());
    for(int i = int(s.size()) - 1; i >= 1; --i)
    {
        if(s[i] + s[i-1] <= k) break;
        ans++;
        s.pop_back();
    }

    return s.back();
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    cin >> n >> k;
    for(int i = 1; i < n; ++i)
    {
        int u, v;
        cin >> u >> v;
        add(u, v); add(v, u);
        dgr[u]++; dgr[v]++;
    }
    for(int i = 1; i <= n; ++i) if(dgr[i] != 1) {rt = i; break;}
    dfs(rt, 0);

    cout << ans + 1 << endl;//remaining elements in vector(rt);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/nishikino-curtis/p/9667268.html
今日推荐