链接:https://ac.nowcoder.com/acm/contest/6874
来源:nowcoder

文章目录

  • A 巨木之森(树的直径)
  • B 乐团派对(贪心)
  • D 巅峰对决(线段树)
  • E 使徒袭来(公式题)
  • F 核弹剑仙(dfs bfs)
  • G 虚空之力(贪心)
  • H 社团游戏(二分 + dp)
  • I 名作之壁(双指针 + 单调队列)
  • J 逃跑路线(思维)

A 巨木之森(树的直径)

  思路: n n n 块区域共有 n − 1 n - 1 n−1 条道路,保证 n n n 块区域联通,我们可以想到如果我们从某一块区域 x x x 出发,现在我们走到了最后一块区域 y y y,那么从 y y y 一定能够到达 x x x,如果我们能够保证 y y y 到 x x x 的距离是最远的,那么我们就可以这个分队走过所有的区域的最短的路径就是 所 有 的 边 − d i s [ x ] [ y ] 所有的边 - dis[x][y] 所有的边−dis[x][y],我们走过了所有的区域在返回起点的时候相当于我们把所有的边都走了一遍,但是我们的目的是遍历所有的区域,如果我们从 x x x 倒退到某一个最远的点 y y y,那么 y y y 就是分队所到达的最后一个点。当我们求出树的直径上的两个端点到各个点的距离,就可以反向求出从每一个点出发需要的走过的最短的路径。

# include<bits/stdc++.h>
using namespace std;typedef long long ll;
const int maxn = 1e5 + 10;
ll dis[maxn][2], ans[maxn], len;
int head[maxn], cnt, maxdot;int n;struct Edge {int u, v,  w, net;
}edge[maxn << 1];void addedge(int u, int v, int w) {edge[cnt].u = u; edge[cnt].v = v;edge[cnt].w = w; edge[cnt].net = head[u];head[u] = cnt ++;
}void dfs(int u, int fa, int num) {if(len <= dis[u][num]) {len = dis[u][num];maxdot = u;}for(int i = head[u]; i != -1; i = edge[i].net) {int v = edge[i].v;if(v == fa) continue;dis[v][num] = dis[u][num] + (ll)edge[i].w;dfs(v, u, num);}
}int main() {ll m; scanf("%d%lld", &n, &m);for(int i = 1; i <= n; ++ i) head[i] = -1;ll edgeSum = 0;for(int i = 1; i < n; ++ i) {int u, v, w; scanf("%d%d%d", &u, &v, &w);addedge(u, v, w); addedge(v, u, w);edgeSum += (ll)w * 2;}//cout << edgeSum << endl;dfs(1, -1, 0); len = 0; dis[maxdot][0] = 0;dfs(maxdot, -1, 0); len = 0;dfs(maxdot, -1, 1);for(int i = 1; i <= n; ++ i) ans[i] = edgeSum - max(dis[i][0], dis[i][1]);sort(ans + 1, ans + n + 1);ll sum = 0;int cnt_ans = 0;for(int i = 1; i <= n; ++ i) {sum += ans[i];if(sum <= m) ++ cnt_ans;}printf("%d\n", cnt_ans);return 0;
}

B 乐团派对(贪心)

  思路:首先如果 a [ i ] > n a[i] > n a[i]>n 成立,那么这位乐手必定无法进入任何一支乐队,此时输出 “-1”,否则,我们对 a [ i ] a[i] a[i] 进行从小到大排序,可以贪心的让最大的 a [ i ] a[i] a[i] 值的乐手先组成一支乐队, 将已经进入乐队的人都进行标记,然后我们从前往后遍历,同时记录当前乐队已经进入了多少人是否满足题意,如果最后又剩余就自动进入最后一支乐队。

#include<bits/stdc++.h>
using namespace std;typedef long long ll;
const int maxn = 2e5 + 10;
int a[maxn];int main() {int n; scanf("%d\n", &n);for(int i = 1; i <= n; ++ i) scanf("%d", &a[i]);int cnt = 0, ans = 0;sort(a + 1, a + n + 1);//for(int i = 1; i <= n; ++ i) cout << a[i] << " "; cout << endl;int inx = -1;for(int i = n; i >= 1; -- i) {++ cnt;if(cnt == a[n]) {++ ans;inx = i;}}if(inx != -1) {cnt = 0;for(int i = 1; i < inx; ++ i) {++ cnt;if(cnt >= a[i]) {++ ans;cnt = 0;}}printf("%d\n", ans);} else puts("-1");return 0;
}

D 巅峰对决(线段树)

  思路:由于在查询的时候区间内的数字都互相不相同,所以我们通过查询区间最大值和区间最小值,通过二者的差值就可以判断这个区间是否可以形成一段连续的数字。线段树:单点修改 + 区间查询

#include<bits/stdc++.h>
using namespace std;
#define LNode x << 1
#define RNode x << 1 | 1typedef long long ll;
const int maxn = 1e5 + 10;
int a[maxn], Max[maxn << 2], Min[maxn << 2];void pushUp(int x) {Max[x] = max(Max[LNode], Max[RNode]);Min[x] = min(Min[LNode], Min[RNode]);
}void build(int l, int r, int x) {Max[x] = Min[x] = 0;if(l == r) {Min[x] = Max[x] = a[l];return ;}int mid = (l + r) >> 1;build(l, mid, LNode);build(mid + 1, r, RNode);pushUp(x);
}void update(int l, int r, int x, int pos, int val) {if(l == r) {Min[x] = Max[x] = val;return ;}int mid = (l + r) >> 1;if(pos <= mid) update(l, mid, LNode, pos, val);else update(mid + 1, r, RNode, pos, val);pushUp(x);
}int query_max(int l, int r, int L, int R, int x) {if(L <= l && R >= r) return Max[x];int mid = (l + r) >> 1, ans = 0;if(L <= mid) ans = max(ans, query_max(l, mid, L, R, LNode));if(R > mid) ans = max(ans, query_max(mid + 1, r, L, R, RNode));return ans;
}int query_min(int l, int r, int L, int R, int x) {if(L <= l && R >= r) return Min[x];int mid = (l + r) >> 1, ans = 1e9 + 10;if(L <= mid) ans = min(ans, query_min(l, mid, L, R, LNode));if(R > mid) ans = min(ans, query_min(mid + 1, r, L, R, RNode));return ans;
}int main() {int n, q; scanf("%d%d", &n, &q);for(int i = 1; i <= n; ++ i) scanf("%d", &a[i]);build(1, n, 1);//for(int i = 1; i <= 4 * n; ++ i) cout << Max[i] << " "; cout << endl;while(q --) {int op; scanf("%d", &op);if(op == 1) {int pos, val; scanf("%d%d", &pos, &val);update(1, n, 1, pos, val);} else {int l, r; scanf("%d%d", &l, &r);int mmax = query_max(1, n, l, r, 1);int mmin = query_min(1, n, l, r, 1);//cout << mmax << " " << mmin << endl;if(mmax - mmin == r - l) puts("YES");else puts("NO");}}return 0;
}

E 使徒袭来(公式题)

  思路: x + y + z 3 > = x ⋅ y ⋅ z 1 3 \frac{x + y + z}{3} >= \sqrt[\frac{1}{3}]{x·y·z} 3x+y+z​>=31​​x⋅y⋅z ​

#include<bits/stdc++.h>
using namespace std;typedef long long ll;int main() {int n; scanf("%d", &n);double ans = 3.0 * pow(n, 1.0/3);printf("%.3f\n", ans);return 0;
}

F 核弹剑仙(dfs bfs)

  思路:根据题意可以建一张图出来,建图完毕后,所问的就是 i i i 点的可以到达的点有多少个。直接 d f s dfs dfs 或者 b f s bfs bfs 可以计算出每一个点的可达点有多少个。

#include<bits/stdc++.h>
using namespace std;typedef long long ll;
const int maxn = 1e3 + 10;
vector<int> G[maxn];
bool vis[maxn];int dfs(int x) {vis[x] = true;int ans = 0;for(int i = 0; i < (int)G[x].size(); ++ i) {int v = G[x][i];if(!vis[v]) {ans += dfs(v);vis[v] = true;++ ans;}}return ans;
}int main() {int n, m; scanf("%d%d", &n, &m);while(m --) {int a, b; scanf("%d%d", &a, &b);G[b].push_back(a);}for(int i = 1; i <= n; ++ i) {for(int j = 1; j <= n; ++ j) vis[j] = false;int ans = dfs(i);    printf("%d\n", ans);}return 0;
}
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
const int maxn = 1e3 + 10;
vector<int> G[maxn];int main() {int n, m; scanf("%d%d", &n, &m);while(m --) {int a, b; scanf("%d%d", &a, &b);G[b].push_back(a);}for(int i = 1; i <= n; ++ i) {int ans = 0;queue<int> q; bool vis[maxn];for(int i = 1; i <= n; ++ i) vis[i] = false;q.push(i); vis[i] = true;while(!q.empty()) {int now = q.front(); q.pop();for(int i = 0; i < (int)G[now].size(); ++ i) {int v = G[now][i];if(!vis[v]) {vis[v] = true;q.push(v);++ ans;}}}printf("%d\n", ans);}return 0;
}

G 虚空之力(贪心)

  思路:直接求出 m i n ( i , j , k ) min(i, j, k) min(i,j,k) 的个数,然后判断最多可以抵消多少个 k k k 即可求出答案。

#include<bits/stdc++.h>
using namespace std;typedef long long ll;
const int maxn = 1e7 + 10;
char a[maxn];int main() {int n; scanf("%d", &n);scanf("%s", a);n = strlen(a);int cntk = 0, cnti = 0, cntn = 0, cntg = 0;for(int i = 0; i < n; ++ i) {if(a[i] == 'k') ++ cntk;else if (a[i] == 'i') ++ cnti;else if(a[i] == 'n') ++ cntn;else if(a[i] == 'g') ++ cntg;}int Min = min(cnti, min(cntn, cntg));if(cntk >= Min) printf("%d\n", Min);else {if(2 * cntk >= Min) printf("%d\n", Min);else printf("%d\n", 2 * cntk);}return 0;
}

H 社团游戏(二分 + dp)

  思路: d p [ i ] [ j ] [ k ] : ( 0 , 0 ) dp[i][j][k]:(0, 0) dp[i][j][k]:(0,0) 到 ( i , j ) (i, j) (i,j) 这个长方形内字母 k k k 的个数是多少个,状态转移方程: d p [ i ] [ j ] [ k ] = d p [ i − 1 ] [ j ] [ k ] + d p [ i ] [ j − 1 ] [ k ] − d p [ i − 1 ] [ j − 1 ] [ k ] + ( s t r [ i ] [ j ] − ′ a ′ = = k ) ; dp[i][j][k] = dp[i - 1][j][k] + dp[i][j - 1][k] - dp[i - 1][j - 1][k] + (str[i][j] - 'a' == k); dp[i][j][k]=dp[i−1][j][k]+dp[i][j−1][k]−dp[i−1][j−1][k]+(str[i][j]−′a′==k);,然后对于每一个位置,我们可以二分一个边长,判断其中的字母的数量是否满足题意即可。

#include<bits/stdc++.h>
using namespace std;typedef long long ll;
const int maxn = 510 + 10;
int dp[maxn][maxn][26];
char str[maxn][maxn];
int x;bool check(int i, int j, int len) {int i1 = i + len - 1, j1 = j + len - 1;for(int k = 0; k < 26; ++ k) {int res = dp[i1][j1][k] - dp[i - 1][j1][k] - dp[i1][j - 1][k] + dp[i - 1][j - 1][k]; if(res > x) return false;}return true;
}int main() {int n, m; scanf("%d%d%d", &n, &m, &x);for(int i = 1; i <= n; ++ i) scanf("%s", str[i] + 1);for(int i = 1; i <= n; ++ i) {for(int j = 1; j <= m; ++ j) {for(int k = 0; k < 26; ++ k) {dp[i][j][k] = dp[i - 1][j][k] + dp[i][j - 1][k] - dp[i - 1][j - 1][k] + (str[i][j] - 'a' == k);}}}/*for(int i = 1; i <= n; ++ i) {for(int j = 1; j <= m; ++ j) {cout << dp[i][j][0] << " ";}cout << endl;}*///cout << "-----------------------------" << endl;for(int i = 1; i <= n; ++ i) {for(int j = 1; j <= m; ++ j) {int l = 1, r = min(m - j + 1, n - i + 1), ans = 0;while(l <= r) {int mid = (l + r) >> 1;if(check(i, j, mid)) {l = mid + 1;ans = mid; } else r = mid - 1;}printf("%d%c", ans, j == m ? '\n' : ' ');}}return 0;
}

I 名作之壁(双指针 + 单调队列)

  思路:如果现在指针 l l l 指向 1 1 1 号位置,现在我们在右边找到一个 r r r 满足最大值和最小值之差大于 k k k,那么之后的每一位置都可以满足最大值和最小值之差大于 k k k,之后的每一个位置都包含满足题意的区间,所以都满足题意,此时就增加了 n − r + 1 n - r + 1 n−r+1 个区间,我们就可以将 l l l 右移,判断最大值和最小值的差,反复的做这样的操作,直到 r r r 到达了最后一个位置为止。

#include<bits/stdc++.h>
using namespace std;typedef long long ll;
const int maxn = 1e7 + 10;
const int mod = 1e9;
int a[maxn];int main() {int n, k; scanf("%d %d", &n, &k);int b, c; scanf("%d%d%d", &a[0], &b, &c);for(int i = 1; i <= n; ++ i) a[i] = (1ll * a[i - 1] * b % mod + 1ll * c) % mod;//for(int i = 1; i <= n; ++ i) cout << a[i] << " "; cout << endl;deque<int> qMax, qMin;ll ans = 0;for(int l = 1, r = 1; r <= n; ++ r) {while(qMax.size() && a[qMax.back()] < a[r]) qMax.pop_back();qMax.push_back(r);while(qMin.size() && a[qMin.back()] > a[r]) qMin.pop_back();qMin.push_back(r);//cout << a[qMax.front()] << " " << a[qMin.front()] << endl;while(a[qMax.front()] - a[qMin.front()] > k) {ans += n - r + 1; l ++;if(qMax.front() < l) qMax.pop_front();if(qMin.front() < l) qMin.pop_front();}}printf("%lld\n", ans);return 0;
}

J 逃跑路线(思维)

  思路:我们知道 ( 2 1 − 1 ) & ( 2 2 − 1 ) & ( 2 3 − 1 ) & ⋅ ⋅ ⋅ & ( 2 n − 1 ) = 1 (2^{1} - 1)\&(2^{2} - 1)\&(2^{3} - 1)\&···\&(2^{n} - 1) = 1 (21−1)&(22−1)&(23−1)&⋅⋅⋅&(2n−1)=1,所以问题就转化为了 横 坐 标 & 1 横坐标 \& 1 横坐标&1,如果横坐标的最后一位为奇数,那么答案则为 1 1 1,否则答案则为 0 0 0。我们只需要判断输入的所有的字符串的最后一位的和,然后判断奇偶即可。

#include<bits/stdc++.h>
using namespace std;typedef long long ll;
const int maxn = 1e4 + 10;
char a[maxn];int main() {int n; scanf("%d", &n);int ans = 0;for(int i = 1; i <= n; ++ i) {scanf("%s", a);int len = strlen(a);//printf("%d\n", len);ans = (ans + a[len - 1]) % 10;}//printf("%d\n", ans);printf("%d\n", ans & 1);return 0;
}

牛客小白月赛27题解(部分)相关推荐

  1. 牛客小白月赛27 题解

    比赛时有点事,赛后补了一下题 A 巨木之森 题意就不复述了, 这题的核心内容就是树的直径. 观察下面的图可以发现,一个点遍历整棵树的路程就是 所有的边*2 - 从这个点出发所到的最远距离.根据树的直径 ...

  2. 牛客小白月赛27 B.乐团派对

    牛客小白月赛27 B.乐团派对 题目链接 题目描述 音乐是带给大家快乐的存在,而你的目标就是组建若干支乐队,让世界听到你们的演奏! 你目前有 nnn 位乐手,每位乐手只能进入一个乐队,但并不是每位乐手 ...

  3. 牛客小白月赛19 题解

    牛客小白月赛19 A. 「水」滔天巨浪(思维) B. 「木」迷雾森林 (数字三角形模型) C. 「土」秘法地震(二维前缀和) F. 「水」悠悠碧波 (字符串) D. 「金」初心如金 (找规律) G. ...

  4. 牛客小白月赛27部分题解(持续更新)

    今天打了一场牛客网的小白月赛,只做出来3题,自己好菜,怎么办呢,只能记录下来,用以砥砺自己,多刷题.QAQ 比赛连接:连接 首先是E题 E-使徒袭来 题目描述 神秘的使徒袭击了第三新东京市,少男少女们 ...

  5. 牛客小白月赛 27部分题解

    已做BCDEFGJ B.乐团派对 刚开始想了个贪心,结果不对然后直接转头想dp了. 将能力值排序. 首先我们先分出来一组,能力值最大的分出来一组人数是ana_nan​即下标是n−an+1→nn-a_n ...

  6. 牛客小白月赛30题解

    A 黑白边 有n个点,m条边,每条边都分为黑边和白边,现在要选一些边,使得所有点都可以相互连通,要求用白边的条数最少. 看到所有的点都相互连通,就可以想到生成树,所以把所有的白边和黑边分类,先用黑色的 ...

  7. 牛客小白月赛65个人题解A-E

    1. 牛客小白月赛65 A. 牛牛去购物 题意:给定n元,购买价格为a元的篮球和价格为b的篮球,数量不定,要使得花掉的钱最多,也就是剩余的钱数最少,求这个值 (1 <= n, a, b < ...

  8. 牛客小白月赛60(A~C)题解

    原题地址:牛客小白月赛60_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ 题目A:小竹与妈妈 题意:输出(x-b)/a即可,没什么好说 代码: #include< ...

  9. F.孤独(牛客小白月赛39)

    F.孤独(牛客小白月赛39) 题意: 给定一棵树,寻找一个路径,将断掉所有与这个路径上的点相连的边,使得剩下的最大连通块的大小最小 题解: 这题有点印象,感觉做过,至少这个方法肯定遇到过 设dp[u] ...

最新文章

  1. Centos版本 32或64位查看命令
  2. [福大软工] W班 总成绩排行榜
  3. 07-XML 文件注解开发
  4. ios 如何在cell中去掉_经典问题:代码中如何去掉烦人的“!=nullquot;判空语句
  5. Django Abstract Models使用下划线设置related_name
  6. 8086汇编常用指令(待解释)
  7. MT7628如何控制GPIO
  8. 一个PHP压缩类,在线压缩文件
  9. 软齿面主要失效形式_齿轮4种常见故障原因,如何采取预防措施,避免齿轮失效...
  10. 【Cocos2d-X(1.x 2.x) 】iOS6与iphone5解决armv7s,Default-568h@2x.png
  11. 淘宝联盟饿了么推广 API取链转链 永久有效
  12. BCNF范式、第四范式和第五范式
  13. Hyper-V虚拟机安装win10系统 2021-10-16
  14. C++获取文件夹内子文件夹名
  15. 基于阶梯灰度图法的空间光调制器相位延迟测量
  16. 【渝粤教育】广东开放大学 数据库应用 形成性考核 (26)
  17. 涂鸦Zigbee SDK开发系列教程——2.环境搭建
  18. 关于C++ variant 类型问题
  19. 基于SLAM的机器人导航避障方案
  20. TCP序列号和确认号

热门文章

  1. 倍福工控机维修控制器主机维修C5102-0060
  2. win7硬盘分区软件_为什么需要为硬盘分区?
  3. 因乐视网IPO财务造假,多位发审委委员被抓;旷视科技完成 4.6亿美元C轮融资丨价值早报
  4. python 100 days github_GitHub - xz333221/Python-100-Days: Python - 100天从新手到大师
  5. [UE4]SplineMesh应用--鼠标轨迹动态生成Mesh
  6. python年龄计算器_使用python的while语句,编写简单门票与年龄计算器
  7. 淘宝京东海外代购低价之谜
  8. Pyppeteer爬取移动端微博评论区简单案例
  9. Java后端学习框架(2020)
  10. C# ffmpeg 录制音视频