牛客小白月赛24题解

题目链接

G.做题

题意:
n 道 题 , 总 共 m 分 钟 n道题,总共m分钟 nm
每 道 题 需 要 花 a i 分 钟 , 最 多 写 多 少 题 每道题需要花a_i分钟,最多写多少题 ai
题解:
排 序 , 每 次 写 时 间 最 小 的 题 , 计 算 一 下 能 写 多 少 道 就 可 以 排序,每次写时间最小的题,计算一下能写多少道就可以
注 意 数 据 范 围 , 要 用 l l 注意数据范围,要用ll ll

AC代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=5e5+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

ll a[maxn];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ll n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++)cin>>a[i];
    sort(a+1,a+1+n);
    ll ans=0;
    for(int i=1;i<=n;i++){
        if(k>=a[i])ans++,k-=a[i];
        if(k<=0)break;
    }
    cout<<ans;
    return 0;
}


F.斗兽棋

题意:
e l e p h a n t − > t i g e r − > c a t − > m o u s e − > e l e p h a n t elephant->tiger->cat->mouse->elephant elephant>tiger>cat>mouse>elephant
每 种 动 物 棋 子 有 如 上 吃 的 关 系 每种动物棋子有如上吃的关系
给 两 种 动 物 , 问 前 者 是 否 赢 或 者 平 局 给两种动物,问前者是否赢或者平局
题解:
将 每 种 动 物 赋 上 一 个 数 0 − 3 , 然 后 二 维 数 组 建 一 个 图 将每种动物赋上一个数0-3,然后二维数组建一个图 03
如 果 能 赢 或 平 局 的 关 系 为 1 , 否 则 为 0 如果能赢或平局的关系为1,否则为0 10

AC代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=5e5+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

map<string,int> m;
int g[4][4]={
    1,1,1,0,
    0,1,1,1,
    1,0,1,1,
    1,1,0,1
};

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    m["elephant"]=1,m["tiger"]=2,m["cat"]=3,m["mouse"]=4;
    string s1,s2;
    cin>>s1>>s2;
    if(g[m[s1]-1][m[s2]-1])cout<<"tiangou yiwusuoyou"<<endl;
    else cout<<"tiangou txdy"<<endl;
    return 0;
}


B.组队

题意:
n 个 人 , 每 个 人 有 各 自 的 能 力 值 n个人,每个人有各自的能力值 n
一 个 队 伍 中 能 力 值 的 极 差 不 能 超 过 k 一个队伍中能力值的极差不能超过k k
问 最 多 可 以 多 少 个 人 一 起 组 队 问最多可以多少个人一起组队
题解:
先 从 小 到 大 排 序 , 枚 举 每 一 个 人 作 为 队 伍 最 小 的 先从小到大排序,枚举每一个人作为队伍最小的
通 过 使 用 u p p e r _ b o u n d 找 出 大 于 k − a i 的 第 一 个 位 置 通过使用upper\_bound找出大于k-a_i的第一个位置 使upper_boundkai
位 置 减 一 到 枚 举 那 个 人 的 个 数 就 是 所 有 能 在 队 伍 里 的 人 位置减一到枚举那个人的个数就是所有能在队伍里的人

AC代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=5e5+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

ll a[maxn];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t;
    cin>>t;
    while(t--){
        int n,k;
        cin>>n>>k;
        for(int i=1;i<=n;i++)cin>>a[i];
        sort(a+1,a+1+n);
        int ans=0;
        for(int i=1;i<=n;i++){
            int p=upper_bound(a+1,a+1+n,k+a[i])-a-1;
            ans=max(ans,p-i+1);
        }
        cout<<ans<<endl;
    }
    return 0;
}


J.建设道路

题意:
, 一 共 有 n 个 城 市 , 每 个 城 市 有 一 个 价 值 a i ,一共有n个城市,每个城市有一个价值a_i ,nai
现 在 需 要 再 每 两 两 城 市 之 间 建 一 条 路 现在需要再每两两城市之间建一条路
这 条 路 的 花 费 为 ( a i − a j ) 2 这条路的花费为(a_i-a_j)^2 (aiaj)2
问 总 共 需 要 多 少 花 费 问总共需要多少花费
题解:
由 于 n 的 值 比 较 大 , 不 能 进 行 n 2 操 作 由于n的值比较大,不能进行n^2操作 nn2
将 公 式 展 开 , a i 2 − 2 ∗ a i ∗ a j + a j 2 将公式展开,a_i^2-2*a_i*a_j+a_j^2 ai22aiaj+aj2
由 于 对 每 个 城 市 来 说 , 都 要 和 另 外 n − 1 个 城 市 建 路 由于对每个城市来说,都要和另外n-1个城市建路 n1
所 以 每 个 城 市 都 要 先 加 上 一 个 ( n − 1 ) ∗ a i 2 的 贡 献 所以每个城市都要先加上一个(n-1)*a_i^2的贡献 (n1)ai2
然 后 只 要 减 去 两 两 城 市 之 间 的 乘 积 的 二 倍 就 可 以 然后只要减去两两城市之间的乘积的二倍就可以
把 所 有 的 乘 积 放 到 一 块 , 其 实 就 是 每 个 城 市 的 价 值 乘 其 他 城 市 价 值 的 和 把所有的乘积放到一块,其实就是每个城市的价值乘其他城市价值的和
所 以 减 去 , 记 得 减 去 值 的 时 候 + m o d 防 止 出 现 负 数 所以减去,记得减去值的时候+mod防止出现负数 +mod

AC代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

ll a[maxn];

int main()
{
    //ios::sync_with_stdio(false);
    //cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    scanf("%d",&n);
    ll sum=0;
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]),sum+=a[i]%mod;
    ll ans=0;
    for(int i=1;i<=n;i++){
        sum=(sum-a[i]+mod)%mod;
        ans=(ans+(n-1)*a[i]%mod*a[i]%mod)%mod;
        ans=(ans-2*a[i]%mod*sum%mod+mod)%mod;
    }
    printf("%lld",ans);
    return 0;
}


I.求和

题意:
存 在 一 棵 有 n 个 结 点 的 树 , 每 个 点 有 一 个 价 值 a i 存在一棵有n个结点的树,每个点有一个价值a_i nai
现 在 需 要 进 行 两 种 操 作 ( 共 m 次 ) 现在需要进行两种操作(共m次) (m)
1. 给 某 一 个 结 点 的 价 值 加 上 x 1.给某一个结点的价值加上x 1.x
2. 查 询 一 个 结 点 和 他 子 树 的 全 部 价 值 2.查询一个结点和他子树的全部价值 2.
题解:
现 在 需 要 进 行 不 断 更 新 和 查 询 现在需要进行不断更新和查询
所 以 第 一 个 想 到 的 就 是 线 段 树 或 者 树 状 数 组 所以第一个想到的就是线段树或者树状数组 线
但 是 我 们 没 办 法 维 护 每 个 结 点 的 子 树 但是我们没办法维护每个结点的子树
就 想 到 了 用 d f s 序 , 给 某 个 结 点 重 新 定 义 就想到了用dfs序,给某个结点重新定义 dfs
并 用 l i 和 r i 之 间 的 数 维 护 的 自 己 到 所 有 子 节 点 并用l_i和r_i之间的数维护的自己到所有子节点 liri
就 是 利 用 D F S 的 特 性 , 完 成 这 个 操 作 就是利用DFS的特性,完成这个操作 DFS
然 后 就 可 以 利 用 树 状 数 组 维 护 和 更 新 前 缀 和 然后就可以利用树状数组维护和更新前缀和
然 后 查 询 的 点 只 要 找 到 这 个 点 i 的 l i 到 r i 的 区 间 大 小 就 可 以 了 然后查询的点只要找到这个点i的l_i到r_i的区间大小就可以了 iliri

AC代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

int l[maxn],r[maxn],cnt,a[maxn];
ll c[maxn];
//树状数组
int lowbit(int x)//返还x的最低位
{
	return x&(-x);
}
ll query(int x)
{
	ll s=0;
	while(x>0)
	{
		s+=c[x];
		x-=lowbit(x);
	}
	return s;
}
void add(int x,ll v)
{
	while(x<maxn)
	{
		c[x]+=v;
		x+=lowbit(x);
	}
}
vector<int> g[maxn];
void dfs(int u,int fa){
    l[u]=++cnt;
    for(auto v:g[u]){
        if(v==fa)continue;
        dfs(v,u);
    }
    r[u]=cnt;
}

int main()
{
    //ios::sync_with_stdio(false);
    //cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        g[u].pb(v);
        g[v].pb(u);
    }
    dfs(k,0);
    for(int i=1;i<=n;i++)
        add(l[i],a[i]);
    while(m--){
        int op;
        scanf("%d",&op);
        if(op==1){
            int a,x;
            scanf("%d%d",&a,&x);
            add(l[a],x);
        }
        else{
            int a;
            scanf("%d",&a);
            printf("%d\n",query(r[a])-query(l[a]-1));
        }
    }

    return 0;
}

H.认认都是好朋友

题意:
你 有 1 e 9 个 手 下 你有1e9个手下 1e9
你 收 到 了 m 张 纸 条 , 每 个 纸 条 上 有 a , b , c 三 个 数 你收到了m张纸条,每个纸条上有a,b,c三个数 mabc
如 果 c = 1 表 示 你 的 手 下 a 和 b 是 朋 友 如果c=1表示你的手下a和b是朋友 c=1ab
否 则 a 和 b 就 是 敌 人 否则a和b就是敌人 ab
( 朋 友 的 朋 友 也 是 朋 友 ) (朋友的朋友也是朋友)
问 纸 条 是 否 会 产 生 矛 盾 的 地 方 ( 两 个 人 是 朋 友 又 是 敌 人 ) 问纸条是否会产生矛盾的地方(两个人是朋友又是敌人)
题解:
最 后 的 提 示 很 明 显 , 这 道 题 是 并 查 集 最后的提示很明显,这道题是并查集
但 是 这 道 题 明 确 说 了 , 你 有 1 e 9 个 手 下 但是这道题明确说了,你有1e9个手下 1e9
所 以 说 明 你 的 a 和 b 非 常 大 , 肯 定 不 能 开 那 么 大 的 数 组 所以说明你的a和b非常大,肯定不能开那么大的数组 ab
所 以 先 把 所 有 的 a 和 b 维 护 一 下 , 进 行 一 个 离 散 操 作 所以先把所有的a和b维护一下,进行一个离散操作 ab
然 后 我 们 需 要 想 办 法 判 断 两 个 人 又 是 朋 友 又 是 敌 人 然后我们需要想办法判断两个人又是朋友又是敌人
我 们 可 以 用 并 查 集 先 维 护 所 有 是 朋 友 的 人 我们可以用并查集先维护所有是朋友的人
然 后 再 找 所 有 是 敌 人 的 人 , 如 果 他 们 已 经 是 朋 友 了 , 说 明 出 现 矛 盾 然后再找所有是敌人的人,如果他们已经是朋友了,说明出现矛盾

AC代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=2e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}
int a[maxn],b[maxn],c[maxn];
int d[maxn<<1],fa[maxn<<1];
int find(int x){
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}

int main()
{
    //ios::sync_with_stdio(false);
    //cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t;
    t=read();
    while(t--){
        int n;
        n=read();
        for(int i=1;i<=n;i++){
            a[i]=read(),b[i]=read(),c[i]=read();
            d[2*i-1]=a[i],d[2*i]=b[i];
            fa[2*i-1]=2*i-1,fa[2*i]=2*i;
        }
        sort(d+1,d+1+2*n);
        int len=unique(d+1,d+1+2*n)-d-1;
        bool f=0;
        for(int i=1;i<=n;i++){
            int x=lower_bound(d+1,d+1+len,a[i])-d;
            int y=lower_bound(d+1,d+1+len,b[i])-d;
            int p=find(x),q=find(y);
            if(c[i])
                fa[q]=p;
        }
        for(int i=1;i<=n;i++){
            int x=lower_bound(d+1,d+1+len,a[i])-d;
            int y=lower_bound(d+1,d+1+len,b[i])-d;
            int p=find(x),q=find(y);
            if(!c[i]&&p==q)f=1;
        }
        if(!f)printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值