AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

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

作者: 作者的头像   wisdom-77 ,  2020-08-12 00:54:12 ,  所有人可见 ,  阅读 655


0


传送门

A.Groundhog and 2-Power Representation

题意

给一个表达式,转为一个10进制数。例如:(2(2)+2+2(0))+2(2+2(0))+2(0) = 137

思路

直接上python

代码

s = input()
ans = ''

for i in s:
    if i == '(': 
        ans += '**('
    else:
        ans += i;
print(eval(ans));

B. Groundhog and Apple Tree

题意

有一颗数,从根节点1开始走,经过所有的点且每条边只能走两次,再回到根节点1.每条边都有一个边权$ w_i $,每一次经过这条边都会消耗$w_i$的HP.每个节点都有一个果子,吃掉这个果子会获得$a_i$的HP,但每个节点的果子只能吃一次.在每个节点,每休息一个单位时间就会恢复1HP.
求在任何时候的HP都为非负数的最少休息时间.
$ 1 \leq T \leq 1000, 1 \leq n \leq 10^{5}, \sum n \leq 10 ^{6}, 0 \leq a_i, w_i \leq 2^{31}$

思路

场上一顿乱贪,事实证明瞎搞还是A不了题的
如何求子结点的遍历顺序?
仔细一想,可以发现,A->B->A包含两种情况
1. $ 消耗值cost \leq 获得值gain$
2. $ 消耗值cost > 获得值gain$

贪心策略
对于只含第1种情况,我们肯定直接按 消耗值cost小 的思路贪心即可
对于只含第2种情况,利用johnson法则,让消耗值的最大值尽量小,即
$$max(a.cost, a.cost - a.gain + b.cost) < max(b.cost, b.cost - b.gain + a.cost)$$
也可化简公式,转化为$ a.gain > b.gain $
对于两种同时包含的情况,肯定优先选择第一种
这题写的时候细节也挺多

代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef pair<long, long> PLL;

const int N = 1e5 + 10;

int t, n, x, y;
ll w, a[N];
struct Tree{
    int v, ne;
    ll w;
}edge[N << 1];
int head[N], tot;
void add(int x, int y, ll w)
{
    edge[++tot].v = y;
    edge[tot].w = w;
    edge[tot].ne = head[x];
    head[x] = tot;
}

struct node{
    int id;
    ll cost, gain;
}d[N];
bool cmp(const node &a, const node &b)
{
    if (a.gain >= a.cost)
    {
        if (b.gain >= b.cost) return a.cost < b.cost;
        else return true;
    }
    else
    {
        if (b.gain >= b.cost) return false;
        else
        {
            ll val1 = max(a.cost, a.cost - a.gain + b.cost);
            ll val2 = max(b.cost, b.cost - b.gain + a.cost);
            return val1 < val2;
        }
        //else return a.gain > b.gain;
    }
}

ll ans = 0;
void dfs(int son, int fa)
{
    vector<node>p; 
    for (int i = head[son]; i; i = edge[i].ne)
    {
        int v = edge[i].v;
        if (v == fa) continue;
        dfs(v, son);
        d[v].id = v;
        d[v].cost += edge[i].w;
        d[v].gain -= edge[i].w;
        if (d[v].gain < 0) d[v].cost -= d[v].gain, d[v].gain = 0;
        p.push_back(d[v]);
    }

    sort(p.begin(), p.end(), cmp);

    d[son].cost = d[son].gain = 0;
    ll tmp = a[son];
    for (auto x: p)
    {
        tmp -= x.cost;
        if (tmp < 0)
        {
            d[son].cost -= tmp;
            tmp = 0;
        }
        tmp += x.gain;
    }
    d[son].gain = tmp;
}

void init(int n)
{
    for (int i = 0; i <= n; i++) head[i] = 0;
    for (int i = 0; i <= n; i++) d[i].id = d[i].cost = d[i].gain = 0;
    tot = 0;
}

int main()
{
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d", &n);
        init(n);
        for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
        for (int i = 1; i < n; i++)
        {
            scanf("%d %d %lld", &x, &y, &w);
            add(x, y, w);
            add(y, x, w);
        }

        dfs(1, 0);  
        printf("%lld\n", d[1].cost);
    }

    return 0;
}

E.Groundhog Chasing Death

题意

$ \prod_{i=a}^b \prod_{j=c}^d gcd(x^i, y^j) $ $ mod $ $ 998244353$
$0 \leq a,b,c,d \leq 3 * 10^{6}, 0 < x,y \leq 10^{9}, a \leq b,c \leq d$

思路

直接枚举质因子,暴力求解即可
场中脑子混乱,假代码满天飞

代码

#include<bits/stdc++.h>
using namespace std;
const int P=998244353;
int qpow(int a,long long b){
    int res=1;
    for(;b;b>>=1){
        if(b&1){
            res=1ll*res*a%P;
        }
        a=1ll*a*a%P;
    }
    return res;
}
int main(){
    int a,b,c,d,x,y;
    cin>>a>>b>>c>>d>>x>>y;
    vector<int>p;
    int D=__gcd(x,y);
    for(int i=2;i<=D/i;i++){
        if(D%i==0){
            p.push_back(i);
            while(D%i==0)D/=i;
        }
    }
    if(D!=1){
        p.push_back(D);
    }
    if(D==P){
        cout<<"0\n";
        return 0;
    }
    vector<int>c1,c2;
    for(auto& i:p){
        int c=0;
        while(x%i==0)x/=i,c++;
        c1.push_back(c);
        c=0;
        while(y%i==0)y/=i,c++;
        c2.push_back(c);
    }
    int ans=1;
    const int N=3e6;
    for(int j=0;j<p.size();j++){
        long long cc=0;
        for(int i=a;i<=b;i++){
            int lim=c1[j]*i;
            int cut=lim/c2[j];
            cut=max(cut,c-1);
            cut=min(cut,d);
            long long x=(cut+c)*(cut-c+1ll)>>1;
            cc+=x*c2[j]%(P-1);
            long long up=1ll*i*(d-cut);
            cc+=up*c1[j]%(P-1);
        }
        ans=1ll*ans*qpow(p[j], cc)%P;
    }
    cout<<ans;
}


I.The Crime-solving Plan of Groundhog

题意

给n个$0 - 9$的数字,将n个数字分成2组,每组生成1个正整数,且不含前导零.求两个数的最小乘积.
$1 \leq T \leq 1000, 2 \leq n \leq 100000, 1 \leq \sum n \leq 1000000$

思路

将最小的非负数划分到一组中;其余数字划分到另一组中,组成一个最小合法正整数.将两数相乘即为答案
注意:高精度乘法

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

vector<int> mul(vector<int> &A, int b)
{   
    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size() || t; i ++ )
    {
        if (i < A.size()) t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }
    return C;
}

int t, n, a[N];
int num[10];

int main()
{
    scanf("%d", &t);
    while (t--)
    {
        int mi = 10;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);

        for (int i = 0; i < 10; i++) num[i] = 0;
        for (int i = 1; i <= n; i++) num[a[i]]++;
        for (int i = 1; i < 10; i++)
            if (num[i]) mi = min(mi, i);

        int lval = mi; num[lval]--;
        vector<int>v;
        int flag = 0;
        for (int i = 1; i < 10 && !flag; i++)
            for (int j = 1; j <= num[i] && !flag; j++)
            {
                v.push_back(i);
                num[i]--;
                flag = 1;
            }
        for (int i = 0; i < 10; i++)
            for (int j = 1; j <= num[i]; j++)
            v.push_back(i);

        reverse(v.begin(), v.end());
        vector<int> ans = mul(v, lval);
        reverse(ans.begin(), ans.end());
        for (auto x: ans)
        {
            printf("%d",x);
        }
        printf("\n");

    }


    return 0;
}

J.The Escape Plan of Groundhog

题意

给一个$n*m$的01矩阵,求满足以下条件的子矩阵个数.
1. 宽度和高度必须大于1
2. 四条边界上的值必须为1
3. 内部(不含边界)的0和1的数量差值(绝对值)小于等于1

$1 \leq n,m \leq 500$

思路

前期把题读错了,把绝对值漏了,导致卡题
从数据范围可得,可支持$O(N^3)$的时间复杂度
将01矩阵中的0记为-1,求一遍二维前缀和,即可得到前缀矩阵1和0的数量差值
由于题中只需求绝对值小于等于1 $~$且$~$n和m的范围很小,那么直接在数组中找对应合法个数即可
首先枚举上下边界,然后用双指针(l,r)的思想将列扫一遍.
r指针遍历到合法的情况就将’前缀’压入数组中,l指针遍历到合法的情况先从数组中删除’前缀’,再求对应合法解。
注意:前缀和可能出现负数,因此需要扩大一倍数组;宽度和高度必须大于1

代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 510;

int n, m;
int mp[N][N], down[N][N], cnt[600010], pre[N][N];

int main()
{
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            scanf("%d", &mp[i][j]);

    for (int i = n; i >= 1; i--)
        for (int j = m; j >= 1; j--)
            if (mp[i][j] == 1) down[i][j] = down[i + 1][j] + 1;
            else down[i][j] = 0;

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if (mp[i][j] == 1) pre[i][j] = pre[i - 1][j] + pre[i][j - 1] - pre[i - 1][j - 1] + 1;
            else pre[i][j] = pre[i - 1][j] + pre[i][j - 1] - pre[i - 1][j - 1] - 1;

    ll ans = 0;
    for (int i = 1; i <= n; i++)
    {
        for (int j = i + 1; j <= n; j++)
        {
            int l = 1, r = 1;
            while (r <= m)
            {
                if (mp[i][r] + mp[j][r] < 2)
                {
                    l++; r++;
                    continue;
                }
                while (r <= m && mp[i][r] && mp[j][r])
                {
                    if (down[i][r] >= j - i + 1) 
                    {
                        cnt[pre[j - 1][r] - pre[i][r] + 300000]++;
                        // if (j == 4) cout << i << " " << j << " " << r << " " << pre[j - 1][r] - pre[i][r] << endl;
                    }
                    r++;
                }
                while (l < r)
                {
                    if (down[i][l] >= j - i + 1) cnt[pre[j - 1][l] - pre[i][l] + 300000]--;
                    else
                    {
                        l++;
                        continue;
                    }
                    int val = pre[j - 1][l] - pre[i][l] + (j - i - 1);
                    // if (j == 4) cout << "query:" << val << endl;
                    val += 300000;
                    ans += cnt[val - 1] + cnt[val] + cnt[val + 1];
                    l++;
                }
                // cout << "l:" << l << endl;    
            }
            // cout << i << " " << j << " " << ans << endl;
        }
    }
    printf("%lld\n", ans);  


    return 0;
}

K.The Flee Plan of Groundhog

题意

有一棵树,树边为1m,Groundhog在节点1,Orange在节点n.前t时刻,Groundhog向n走(沿最短路径),速度1m/s,Orange不动.从t时刻开始,Orange开始抓Groundhog,Groundhog速度仍为1m/s,Groundhog也可以选择原地不动,而Orange速度为2m/s.问最长多少秒Orange能抓到Groundhog.

$1 \leq n \leq 10^{5}, 1 \leq t \leq n-1, 1 \leq x,y \leq n$

思路

首先,由题意可得Groundhog在t时刻的位置.
t时刻后,Orange肯定要尽量把Groundhog逼到角落上去,那么不妨把Orange所在的节点n设为根节点.
对于Groundhog而言,若此时刻有多条路可走,尽量要走到一条远离Orange的最长链上去.
那么,我们可以枚举t时刻后,若Groundhog继续沿原路径(最短路径)走所在的节点,如果Groundhog此时还未被Orange抓住,则挑一条最长链跑,求最长被抓到的时间.最后将枚举所求得的所有时间取最大值即可.

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=1e5+10;
int head[N],cnt,n,t,dep[N],root,dep1[N],maxx;
bool ok[N];
struct edge{
    int next,to;
}e[N<<1];
void add(int u,int v){
    e[++cnt].next=head[u];
    e[cnt].to=v;
    head[u]=cnt;
}
void dfs(int u,int f){
    dep[u]=dep[f]+1;
    if(u==n)return;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==f)continue;
        dfs(v,u);
        ok[u]=ok[u]|ok[v];
    }
}
void dfs1(int u,int f)
{
    dep1[u]=dep1[f]+1;
    maxx=max(maxx,dep1[u]);
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==f||ok[v])continue;
        dfs1(v,u);
    }
}
int main()
{
    scanf("%d %d",&n,&t);
    ok[n]=1;
    for(int i=1;i<n;i++){
        int a,b;scanf("%d %d",&a,&b);
        add(a,b);add(b,a);
    }
    dfs(1,0);
    if(dep[n]-dep[1]<=t){
        puts("0");
        return 0;
    }

    for(int i=1;i<=n;i++){
        if(ok[i]&&dep[i]-dep[1]<t)ok[i]=0;
        if(ok[i]&&dep[i]-dep[1]==t)root=i;
    }

    int ans=0;
    int z=dep[n]-dep[1]-t;
    for(int i=1;i<=n;i++){
        if(i==n)continue;
        if(ok[i]){
            maxx=0;
            dfs1(i,0);
            int x=maxx-dep1[i];
            int y=z-3*(dep[i]-dep[root]);
            if(y<=0)continue;
            if(x<=y)ans=max(ans,(x+y+1)/2);
            else ans=max(ans,y+dep[i]-dep[root]);
        }
    }
    printf("%d\n",ans);
}

0 评论

App 内打开
你确定删除吗?
1024
x

© 2018-2025 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息