2020牛客暑期多校训练营(第九场)

题号 A B C D E F G H I J K L
赛中 🎈 🎈 🎈 🎈 💭
赛后


A - Groundhog and 2-Power Representation

题意

计算特定的表达式,答案范围为\([10,10^{180}]\)

分析

利用大数和栈模拟即可,也可以转化为合法表达式,直接用Python的eval()方法计算。

代码
s = input()

st = [0]

for c in s:
    if c is '(' or c is '+':
        st.append(c)
    elif c is ')':
        y = 0
        while True:
            x = st.pop()
            if x is '+':
                continue
            elif x is '(':
                break
            else:
                y += int(x)
        x = st.pop()
        st.append(x ** y)
    else:
        x = st.pop()
        if x not in ['(', ')', '+']:
            x = int(x) * 10 + int(c)
            st.append(int(x))
        else:
            st.append(x)
            st.append(int(c))

ans = 0
for x in st:
    if x is not '+':
        ans += x

print(ans)
        

B - Groundhog and Apple Tree

题意

给出一棵\(n\)个结点的树,到达第\(i\)个结点能够恢复\(a_i\)点体力(每个结点仅能恢复\(1\)次体力),每次经过第\(i\)条边需要消耗\(w_i\)点体力(每条边仅能经过\(2\)次),每休息\(1\)秒可以恢复\(1\)点体力(在任意结点处均可休息),体力时刻不可小于\(0\),问从\(1\)号点出发,初始体力为\(0\),遍历完所有结点并回到\(1\)号点最少需要休息多少秒?(\(1\le n\le 10^5, \sum n \le 10^6, 0 \le a_i, w_i \lt 2^{31}\)

分析

首先有一个重要的推论,那就是 在起点把需要休息恢复的体力都恢复够再出发一定更优。考虑如果需要在中途休息恢复体力,那么显然在起点先恢复好这部分体力再出发只会令结果不变或更优。

则可以设:

\[dp[u]=遍历以u为根的子树所需要预先恢复的体力\\ sum[u]=\sum_{a,w\in 以u为根的子树} (a - 2*w) \]

对于当前结点\(u\),其儿子为\(v_i\),连边边权为\(w_i\),则结合\(dp[v_i]\)\(sum[v_i]\)可以进一步求得 \(u\)开始遍历完以\(v_i\)为根的子树后返回\(u\) 所需要 预先支付的代价\(in[v_i]\)最后得到的收益\(out[v_i]\)

接下来就是决定子树遍历的顺序,为使得\(dp[u]\)(借用的代价)最小,显然应当先遍历\(in[v_i]\le out[v_i]\)的子树,且要按\(in[v_i]\)从小到大遍历;随后遍历\(in[v_i]\gt out[v_i]\)的子树,且要按\(out[v_i]\)从大到小遍历。

最终\(dp[1]\)即为答案,时间复杂度\(O(n\log n)\)

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int MOD = 998244353;
const int maxn = 1e5 + 10;
int n, a[maxn];
struct edge
{
    int v;
    int w;
};
struct sub_tree
{
    LL in;
    LL out;
};
bool cmp1(const sub_tree x, const sub_tree y)
{
    if(x.in != y.in)
        return x.in < y.in;
    else
        return x.out > y.out;
}
bool cmp2(const sub_tree x, const sub_tree y)
{
    if(x.out != y.out)
        return x.out > y.out;
    else
        return x.in < y.in;
}
vector<edge> g[maxn];
LL dp[maxn], sum[maxn];
void init()
{
    for(int i = 1; i <= n; i++)
    {
        dp[i] = sum[i] = 0;
        g[i].clear();
    }
}
void DFS(int u, int fa)
{
    vector<sub_tree> b, c;
    sum[u] = a[u];
    for(auto e : g[u])
    {
        int v = e.v, w = e.w;
        if(v == fa)
            continue;
        DFS(v, u);
        sum[u] += sum[v] - (long long)2 * w;
        sub_tree t;
        if(dp[v] + sum[v] >= w)
            t = sub_tree{w + dp[v], dp[v] + sum[v] - w};
        else
            t = sub_tree{(long long)2 * w - sum[v], 0};
        if(t.in <= t.out)
            b.push_back(t);
        else
            c.push_back(t);
    }
    sort(b.begin(), b.end(), cmp1);
    sort(c.begin(), c.end(), cmp2);
    LL cur = a[u];
    for(auto t : b)
    {
        if(cur < t.in)
        {
            dp[u] += t.in - cur;
            cur = t.in;
        }
        cur = cur - t.in + t.out;
    }
    for(auto t : c)
    {
        if(cur < t.in)
        {
            dp[u] += t.in - cur;
            cur = t.in;
        }
        cur = cur - t.in + t.out;
    }
}
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        init();
        for(int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        for(int i = 1; i <= n - 1; i++)
        {
            int u, v, w;
            scanf("%d %d %d", &u, &v, &w);
            g[u].push_back(edge{v, w});
            g[v].push_back(edge{u, w});
        }
        DFS(1, 0);
        printf("%lld\n", dp[1]);
    }
    return 0;
}


C - Groundhog and Gaming Time

题意
思路
代码

D - Groundhog and Golden Apple

题意
思路
代码

E - Groundhog Chasing Death

题意
思路
代码

F - Groundhog Looking Dowdy

题意

\(n\)天,每天有\(k_i\)件物品,分别有权值\(a_{i,j}\),问从中选\(m\)天,每天选\(1\)件物品,所选的\(m\)件物品中权值的最大差值最小为多少?(\(1\le a_{i,j} \le 10^9, 1\le m \le n\le 10^6, k_i \ge 1 且 \sum k_i \le 2\cdot 10^6\)

分析

将物品按权值排序,对于一个权值区间,若其中包含的物品对应天数种类\(\ge m\),则为一个合法区间。所以只需要找到一个最小的合法区间,该过程显然具有单调性,可用尺取法在\(O(n)\)的时间复杂度内解决。

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define fir first
#define sec second
const int INF = 0x3f3f3f3f;
const int MOD = 998244353;
const int maxn = 2e6 + 10;
int n, m,N;
pii X[maxn];
int cnt[maxn], tot;
int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++)
    {
        int k, d;
        scanf("%d", &k);
        for(int j = 1; j <= k; j++)
        {
            scanf("%d", &d);
            X[++N] = pii(d,i);
        }
    }
    sort(X + 1,X + N + 1);
    if (m == 0) puts("0");
    else {
        int l = 1, r = 0, tot = 0, ans = 1000000000;
        cnt[X[r].sec]++;
        for (int l = 1; l <= N; l++) {
            while (tot < m && r < N) {
                ++r;
                if (cnt[X[r].sec] == 0) tot++;
                cnt[X[r].sec]++;
            }
            if (tot >= m) ans = min(ans,X[r].fir - X[l].fir);
            cnt[X[l].sec]--;
            if (cnt[X[l].sec] == 0) tot--;
             
        }
        printf("%d\n", ans);
    }
    return 0;
}

G - Groundhog Playing Scissors

题意
思路
代码

H - Groundhog Speaking Groundhogish

题意
思路
代码

I - The Crime-solving Plan of Groundhog

题意
思路
代码

J - The Escape Plan of Groundhog

题意

给出一个\(n\times m\)的01矩阵,问满足下列条件的子矩阵的个数:(\(1\le n, m \le 500\)

  1. 子矩阵边界位置全为\(1\)
  2. 子矩阵内部(不含边界)的\(0\)\(1\)数量差的绝对值不大于\(1\)
  3. 子矩阵的长和宽均大于\(1\)
分析

考虑枚举上、下边界所在行的位置,遍历行时判断上、下边界\(1\)的连续性即可保证上、下边界为全\(1\),预处理前缀\(0\)\(1\)数量之和,即可\(O(1)\)判断左、右边界是否为全\(1\),遍历的时候同时维护一下区域内的前缀\(0\)\(1\)数量差之和,保证数量差的绝对值不大于\(1\)

最终时间复杂度\(O(n^2m)\)

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int MOD = 998244353;
const int maxn = 500 + 10;
const int fix = 300000;
int n, m, a[maxn][maxn];
int sum[2][maxn][maxn], cnt[1000010];
vector<int> v;
void clear()
{
    for(auto d : v)
        cnt[d]--;
    v.clear();
}
int main()
{
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            scanf("%d", &a[i][j]);
            sum[0][i][j] = sum[0][i-1][j] + (a[i][j] == 0 ? 1 : 0);     // 纵向1数量前缀和
            sum[1][i][j] = sum[1][i-1][j] + (a[i][j] == 1 ? 1 : 0);     // 纵向0数量前缀和
        }
    }
    LL ans = 0;
    for(int i = 1; i <= n; i++)
    {
        for(int j = i + 1; j <= n; j++)
        {
            int d = fix;    // 区域内前缀0、1数量之差
            for(int k = 1; k <= m; k++)
            {
                if(!a[i][k] || !a[j][k])    // 上、下边界的1断连
                {
                    clear();
                    d += (sum[0][j-1][k] - sum[0][i][k]) - (sum[1][j-1][k] - sum[1][i][k]);
                }
                else if(sum[0][j-1][k] - sum[0][i][k] == 0)  // 右边界全1
                {
                    ans += cnt[d-1] + cnt[d] + cnt[d+1];
                    d += (sum[0][j-1][k] - sum[0][i][k]) - (sum[1][j-1][k] - sum[1][i][k]);
                    //printf("i = %d  j = %d  k = %d  ans = %lld", i, j, k, ans);
                    cnt[d]++;
                    v.push_back(d);
                }
                else
                    d += (sum[0][j-1][k] - sum[0][i][k]) - (sum[1][j-1][k] - sum[1][i][k]);
             //  printf("  d = %d\n", d - fix);
            }
            clear();
        }
    }
    printf("%lld\n", ans);
    return 0;
}


k - The Flee Plan of Groundhog

题意
思路
代码

L - The Shopping Plan of Groundhog

题意
思路
代码
posted @ 2020-08-10 09:49  ncu_supernova  阅读(212)  评论(0)    收藏  举报