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

10 篇文章 1 订阅

题目链接

B - Groundhog and Apple Tree
题意: 给你一个棵以1为根的树,每条边有障碍物,经过时会减少一定的hp,如果吃了第i个苹果,则会回复 a i a_i ai,当没有苹果的时候,可以在原地休息一个单位时间,回复1hp,hp不能为负,但可以是0到正无穷大。一个苹果只能吃一次,但每次经过一条边时,都会消耗hp。边很脆弱,每条边只能最多经过2次。从根节点1开始出发,初始hp为0,要求经过所有点后返回1号点,求最少休息时间。
思路: 首先直觉告诉我们肯定要在起点储存足够的hp值,再开始行动是最优的。然后我们每次走每个点的子树顺序会影响到结果,所以我们需要决定如何走才是最优的
在这里插入图片描述

#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<stack>
#include<queue>
#define INF 0x3f3f3f3f
#define lowbit(x) x & -x
#define lson root<<1,l,mid
#define rson root<<1|1,mid+1,r
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=1e5+5;
vector<pair<int,int> >G[N];
ll dp[N],sum[N];
int a[N];

struct node{
    ll in,out;
};

bool cmp1(node q,node qq){
    if(q.in!=qq.in)
        return q.in<qq.in;
    return q.out>qq.out;
}

bool cmp2(node q,node qq){
    if(q.out!=qq.out)
        return q.out>qq.out;
    return q.in<qq.in;
}

void dfs(int u,int fa){
    vector<node>tmp1,tmp2;
    sum[u]=a[u];
    for(auto v:G[u]){
        if(v.first==fa)
            continue;
        dfs(v.first,u);
        sum[u]+=sum[v.first]-2ll*v.second;
        node tmp;
        if(dp[v.first]+sum[v.first]>=v.second){
            tmp.in=v.second+dp[v.first];
            tmp.out=sum[v.first]+dp[v.first]-v.second;
        }else{
            tmp.out=0;
            tmp.in=2ll*v.second-sum[v.first];
        } 

        if(tmp.in<=tmp.out)
            tmp1.push_back(tmp);
        else 
            tmp2.push_back(tmp);
    }
    sort(tmp1.begin(),tmp1.end(),cmp1);
    sort(tmp2.begin(),tmp2.end(),cmp2);
    ll cur = a[u];
    for(auto t : tmp1){
        if(cur < t.in){
            dp[u] += t.in - cur;
            cur = t.in;
        }
        cur = cur - t.in + t.out;
    }
    for(auto t : tmp2){
        if(cur < t.in){
            dp[u] += t.in - cur;
            cur = t.in;
        }
        cur = cur - t.in + t.out;
    }
}

int main(){
#ifdef Mizp
    freopen("in.txt","r",stdin);
#endif
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d",&n);
        for(int i=0;i<=n;i++){
            G[i].clear();
            dp[i]=sum[i]=0;          
        }
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        for(int i=1;i<n;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            G[u].push_back({v,w});
            G[v].push_back({u,w});
        }
        dfs(1,0);
        printf("%lld\n",dp[1]);
    }
    return 0;
}

E - Groundhog Chasing Death
题意: 给你a,b,c,d,x,y,求
∏ i = a b ∏ j = c d g c d ( x i , y j ) \prod_{i=a}^{b}\prod_{j=c}^{d}{gcd(x^i,y^j)} i=abj=cdgcd(xi,yj)
思路: 他人详细题
在比赛的时候,由于听不懂队友的做法,所以直接丢给队友做了,分别对x,y进行质因数分解之后,算出x,y的公共质因子的贡献,由于比赛时队友没有意识到底数相同,将每个质因数的贡献次数相加再进行欧拉降幂,最后再用快速幂求出真正的贡献
所以在需要进行许多次快速幂的时候,我们要意识到是否能进行递推,或者将底数的相同的所有次幂相加后进行欧拉降幂,然后变成每个底数只需要算一次快速幂,可以降低复杂度

#include <bits/stdc++.h>
#define lowbit(i) ((i)&(-i))
#define ls (p << 1)
#define rs (p << 1 | 1)
using namespace std;

typedef unsigned long long ull;
typedef long long ll;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;

const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int N = 1e6;
const ll M = 998244353;
bool vis[N + 15];
ll prime[N + 15], xp[105], yp[105], cntx[105], cnty[105];
int pcnt = 0;
void Euler(){
    vis[0] = vis[1] = 1;
    for(int i = 2; i <= N; i++){
        if(!vis[i]) prime[pcnt++] = i;
        for(int j = 0; 1ll * i * prime[j] <= N; j++){
            vis[i * prime[j]] = 1;
            if(i % prime[j] == 0) break;
        }
    }    
}
ll quick_pow(ll a, ll b){
    ll res = 1;
    while(b > 0){
        if(b & 1)  res = (res * a) % M;
        a = (a * a) % M;
        b >>= 1;
    }
   return res;
}
int main() {

    Euler();
    int a, b, c, d, x, y;
    scanf("%d%d%d%d%d%d", &a,&b,&c,&d,&x,&y);
    if(a > b) swap(a, b);
    if(c > d) swap(c, d);
    int numx = 0, numy = 0;
    // 对x质因数分解
    ll tmpx = x;
    for(int i = 0; i < pcnt && tmpx / prime[i] >= prime[i]; i++) {      
        if(tmpx % prime[i] == 0) {
            xp[++numx] = prime[i];
            while(tmpx % prime[i] == 0) {
                tmpx /= prime[i];
                cntx[numx]++;
            }
        }
    }
    if(tmpx != 1 && tmpx != 0) {
        xp[++numx] = tmpx;
        cntx[numx] = 1;
    }

    // 对y质因数分解
    ll tmpy = y;
    for(int i = 0; i < pcnt && tmpy / prime[i] >= prime[i]; i++) {      
        if(tmpy % prime[i] == 0) {
            yp[++numy] = prime[i];
            while(tmpy % prime[i] == 0) {
                tmpy /= prime[i];
                cnty[numy]++;
            }
        }
    }
    if(tmpy != 1 && tmpy != 0) {
        yp[++numy] = tmpy;
        cnty[numy] = 1;
    }


    ll res = 1;
    for(int i = 1; i <= numx; i++) {
        for(int j = 1; j <= numy; j++) {
            if(xp[i] != yp[j]) continue;
            ll sum = 0;
            for(ll nx = a; nx <= b; nx++) {
                ll ny = (nx * cntx[i] + cnty[j] - 1) / cnty[j];

                if(ny <= c) {
                    sum = (sum + nx * cntx[i] * (d - c + 1)) % (M - 1);
                }
                else if(ny > d) {
                    ll num = 1ll * (c + d) * (d - c + 1) / 2;
                    sum = (sum + cnty[j] * num) % (M - 1);
                }
                else {
                    ll num = (c + ny - 1) * (ny - c) / 2;
                    sum = (sum + cnty[j] * num) % (M - 1);

                    sum = (sum + nx * cntx[i] * (d - ny + 1)) % (M - 1);
                }
            }
            // printf("%lld %lld\n", xp[i], sum);
            res = (res * quick_pow(xp[i], sum)) % M;
        }
    }
    printf("%lld\n", res);

    return 0;
}


J - The Escape Plan of Groundhog
题意: 给你一个n*m的01矩阵(1<=n,m<=500),求存在多少个子矩阵,使得子矩阵边界都为1,且内部的0和1的数目相差不超过1(不含四周边界)。
思路: 由于数据范围,我们可以想一个O(n^3)的思路。
我们可以预处理每一列的1的个数,前缀和即可。
然后进行枚举O(n^2)枚举子矩阵上下边界,然后对O(n)遍历每行中符合的位置即可,我们能快判断出边界是否全为1,但是我们如何内部0与1的个数之差呢?
我们可以开个桶,存下每次确定一个边界全为1时,就统计一下桶里有没有符合的边界与该边界组成新的子矩阵,然后枚举上下行的边界不为1时,记得清空即可
需要注意的是因为0与1的个数有可能为负数,我们可能会用map或unordermap来当作桶,这样就多了O(log)的复杂度,且常数比较大,就会TLE,我们可以开个数组当桶即可,每次右移一个较大的整数,使得不会出现负数的情况即可

#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<stack>
#include<queue>
#define INF 0x3f3f3f3f
#define lowbit(x) x & -x
#define lson root<<1,l,mid
#define rson root<<1|1,mid+1,r
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=1e6+5;
int sum[505][505],a[505][505];
int num[N];
vector<int>tmp;
void cl(){
    for(int it: tmp){
        num[it]--;
    }
    tmp.clear();
}

int main(){
#ifdef Mizp
    freopen("in.txt","r",stdin);
#endif
    int n,m;
    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[i][j]=sum[i-1][j];
            if(a[i][j]==1)
                sum[i][j]+=1;
        }
    }
    ll ans=0;
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            int cnt=3e5;
            for(int k=1;k<=m;k++){
                if(!a[i][k]||!a[j][k]){
                    cnt=3e5;
                    cl();
                }else if(sum[j-1][k]-sum[i][k]==j-i-1){
                    ans+=num[cnt]+num[cnt-1]+num[cnt+1];
                    cnt+=2*(sum[j-1][k]-sum[i][k])-(j-i-1);
                    num[cnt]++;
                    tmp.push_back(cnt);
                }else{
                    cnt+=2*(sum[j-1][k]-sum[i][k])-(j-i-1);
                }
            }
            cl();
        }
    }
    printf("%lld\n",ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值