Educational Codeforces Round 125 (Rated for Div. 2)(ABCDE)

Educational Codeforces Round 125 (Rated for Div. 2)(ABCDE)

A. Integer Moves

在这里插入图片描述
题意:大概是说最初在点(0,0),想要到点(x,y),只能走整数距离(不清楚,根据样例猜),问最少多少步
思路:
目标点就是原点,步数0
目标点与原点的直线距离是整数,步数1
否则步数就是2(先走到同x,再同y)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int x,y;

void solve(){
    scanf("%d%d",&x,&y);
    if(x==0&&y==0) puts("0");
    else{
        int num=sqrt(x*x+y*y);
        if(num*num==x*x+y*y) puts("1");
        else puts("2");
    }
}

int main(){
    int T;scanf("%d",&T);
    while(T--) solve();
    return 0;
}

B. XY Sequence

在这里插入图片描述
题意:构造一个长度为n的序列,序列中每个元素的值 < = B <=B <=B,后一个数 a [ i + 1 ] = a [ i ] + x a[i+1]=a[i]+x a[i+1]=a[i]+x a [ i + 1 ] = a [ i ] − y a[i+1]=a[i]-y a[i+1]=a[i]y,问序列的最大总和是多少
思路:我们知道初始 a [ 0 ] = 0 a[0]=0 a[0]=0,我们每次去找到符合题意的最大的数就行了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
ll n,B,x,y;

void solve(){
    scanf("%lld%lld%lld%lld",&n,&B,&x,&y);
    ll sum=0,now=0;
    for(ll i=1;i<=n;i++){
        ll nu1=now+x;
        ll nu2=now-y;
        if(nu1<=B&&nu2<=B){
            if(nu1>=nu2){
                now=nu1;
                sum+=nu1;
            }
            else{
                now=nu2;
                sum+=nu2;
            }
        }
        else if(nu1<=B){
            now=nu1;
            sum+=nu1;
        }
        else {
            now=nu2;
            sum+=nu2;
        }
    }
    printf("%lld\n",sum);
}

int main(){
    int T;scanf("%d",&T);
    while(T--) solve();
    return 0;
}

C. Bracket Sequence Deletion

在这里插入图片描述
题意:只包含 ′ ( ′ 和 ′ ) ′ '('和')' ()的长度为n的字符串,每次删除前面最短的合法括号序列或回文序列,问最终会操作多少次和最后剩下多少字符
题意很重要,开始读的十分离谱,什么最小剩余的同时的最少操作次数
思路:
我们发现,当前是 ′ ( ′ '(' (,只要后还存在字符,那么都会一起直接删掉。 ( ( 或 ( ) (( 或() ((()
当前是 ′ ) ′ ')' ),后面是 ′ ) ′ ')' ),则构成 ) ) )) ))直接删掉;
而后面如果是 ′ ( ′ '(' (,则一直往后面找,知道找到第一个 ′ ) ′ ')' )或末尾为止。 ) ( ( . . ( ( ) )((..(() )((..(()

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+5;
int n;
char s[N];

void solve(){
    scanf("%d",&n);
    scanf("%s",s+1);
    int use=0,cnt=0;
    for(int i=1;i<=n;){
        if(s[i]=='('){
            if(i!=n){ //  "(("或"()"
                cnt++;
                use+=2;
            }
            i=i+2;
        }
        else{
            int pos=i+1;
            int num=0;
            while(pos<=n&&s[pos]=='('){
                pos++;
                num++;
            }
            if(pos<=n&&s[pos]==')'){ //")(((...(()"
                cnt++;
                use+=num+2;
            }
            i=pos+1;
        }
    }
    printf("%d %d\n",cnt,n-use);
}

int main(){
    int T;scanf("%d",&T);
    while(T--) solve();
    return 0;
}

D. For Gamers. By Gamers.

在这里插入图片描述
题意:
有n种伙伴可以选择,第i种伙伴有 c i , h i , d i ci,hi,di ci,hi,di三个参数,代表选择一个伙伴的花费、每个伙伴的单位时间的攻击力、每个伙伴的生命值。
一共有m个怪物,第j个怪物有 D j , H j Dj,Hj Dj,Hj两个参数,代表每个怪兽的单位时间的攻击力、每个怪兽的生命值。
每一轮我们有C枚金币,我们当前轮只能选择一种伙伴进行战斗,在不超过C枚金币的情况下,我们可以选择多位该类型伙伴,问在没有伙伴阵亡的情况下击败每个怪物的最小花费。 攻击伤害是持续累加的(奇怪)
思路:
我们经过公式转换,我们发现我们可以找到每个金币可以解决的最大怪物乘积???,然后二分答案就行了。
(具体看推导吧)

考虑:
对于第i种伙伴,有ci,hi,di
对于第j个怪物,有Dj,Hj

发现,怪物肯定会十分聪明,只会盯着一个伙伴进行攻击,这样使得我们不满足没有伙伴阵亡的情况,
从而需要增加伙伴数量来加大火力,间接导致花费变高。
我们知道一位伙伴存活时间为: time=hi/Dj, 则我们击败怪物的时间需要严格小于time
设我们需要该类型伙伴数量num,则有: num*time*di>Hj  即num*hi*di>Dj*Hj 
花费为money=num*ci
所以当我们对于花费val元,找到最大的hi*di,就可以解决问题
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+5,M=1e6+6;
ll n,C,m,f[M]; //f[i]表示花费i元的最大h*d
struct node1{ll c,d,h;}a[N];
struct node2{ll D,H;}b[N];

void solve(){
    scanf("%lld%lld",&n,&C);
    for(ll i=1;i<=n;i++){
        scanf("%lld%lld%lld",&a[i].c,&a[i].d,&a[i].h);
        f[a[i].c]=max(f[a[i].c],a[i].d*a[i].h);
    }
    scanf("%lld",&m);
    for(ll i=1;i<=m;i++) scanf("%lld%lld",&b[i].D,&b[i].H);

    for(ll i=1;i<=C;i++){
        for(ll j=1;j<=C/i;j++){
            f[i*j]=max(f[i*j],f[i]*j);
        }
    }
    
    for(ll i=1;i<M;i++) f[i]=max(f[i],f[i-1]);

    for(ll i=1;i<=m;i++){
        ll idx=upper_bound(f+1,f+M,b[i].H*b[i].D)-f;
        if(idx>C) printf("-1 ");
        else printf("%lld ",idx);
    }
    
}

int main(){
    int T=1;
    while(T--) solve();
    return 0;
}

E. Star MST

在这里插入图片描述题意:
在这里插入图片描述
题意:
有n个节点的完全图,问与节点1相连的所有的权值和等于这n个节点构成的最小生成树的图有多少种。
每条边的权值在[1,k]之间,只要有一条边的权值不同,则是不同的两个图
思路:
初始想成在定节点1相连的边的最大权值和最大权值边的数量去解决,发现会少一些情况。
根据大佬思路:
定义:
d p [ i ] [ j ] dp[i][j] dp[i][j]为所有边权小于等于i的边,已经和1相连的边为j个
我们发现对于边权为i+1的边,我们考虑有l个点与节点1的边为i+1
当前剩下 n − 1 − j n-1-j n1j个点,选其中l个, C [ n − 1 − j ] [ l ] C[n-1-j][l] C[n1j][l]
对于这k个点内部的两两连边,有 l ∗ ( l − 1 ) / 2 l*(l-1)/2 l(l1)/2条边,且值需要大于 > = i + 1 >=i+1 >=i+1, 即 ( k − i + 1 ) ( l − 1 ) / 2 (k-i+1)^{(l-1)/2} (ki+1)(l1)/2
对于其他已经与节点1连边的j个点,有 j ∗ l j*l jl条边,且值需要大于 > = i + 1 >=i+1 >=i+1, 即 ( k − i + 1 ) j ∗ l (k-i+1)^{j*l} (ki+1)jl

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=260;
const ll mod=998244353;
ll n,k,sum;
ll dp[N][N];
ll jiec[N+5],inv[N+5];

ll ksm(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}

void init(){
    jiec[0]=1;
    for(ll i=1;i<=N;i++) jiec[i]=jiec[i-1]*i%mod;

    inv[N]=ksm(jiec[N],mod-2);
    for(ll i=N-1;i>=0;i--) inv[i]=inv[i+1]%mod*(i+1)%mod;
}

ll C(ll a,ll b){return a<b?0:jiec[a]*inv[a-b]%mod*inv[b]%mod;}

void solve(){
    init();
    scanf("%lld%lld",&n,&k);
    dp[0][0]=1;
    for(ll i=1;i<=k;i++){
        for(ll j=0;j<n;j++){
            for(ll l=0;l+j<n;l++){
                dp[i][j+l]=(dp[i][j+l]+dp[i-1][j]*C(n-1-j,l)%mod*ksm(k-i+1,(l-1)*l/2+l*j))%mod;
            }
        }
    }
    printf("%lld\n",dp[k][n-1]);
}

int main(){
    int T=1;
    while(T--) solve();
    return 0;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值