2021.5.10
省赛结束了意味上半年的比赛基本上也算告一段落了,这场比赛也让我见识到了很多选手队伍在比赛中的队友之间的相互协作,互相鼓励,互相纠正;也发现了很多自己的不足:思维能力不够,算法应用不准确,刷题数量远远不够等很多情况,还有与队友合作间一些思维的激烈碰撞等等情况。为了更好的备战以后的比赛,以后每场cf无特殊情况每场都打每场都补,保证每日一题,如果实在没时间也要保证每日一道简单的cf a;暑假前基础算法模版掌握牢固,暑假进击提高;蓝桥杯国赛还有一个月,这个月内对暴力暴搜DP进行集中特训。希望下半年能取得一个好成绩吧。继续加油!
别骂了别骂了,来补了呜呜😭😭😭
参考:
https://blog.csdn.net/messywind/article/details/116598341
https://blog.csdn.net/weixin_45697774/article/details/116902043
文章目录
- [B - Build Roads](https://ac.nowcoder.com/acm/contest/15600/B)
- [C - Cat Virus](https://ac.nowcoder.com/acm/contest/15600/C)
- [D - Dyson Box](https://ac.nowcoder.com/acm/contest/15600/D)
- [G - Grade Point Average](https://ac.nowcoder.com/acm/contest/15600/G)
- [H - Adventurer's Guild](https://ac.nowcoder.com/acm/contest/15600/H)
- [M - Matrix Problem](https://ac.nowcoder.com/acm/contest/15600/M)
B - Build Roads
题意
根据给定的算法生成的点的值作为点值,构成一个无向图,边权是两个点的gcd,求这个无向图的MST
题解
如果给出的随机数之间有质数的话,因为我们是由一个完全图来构造最小生成树的,只要有一个点是质数了,那么其他点到这个数的边权一定全是1。那么就以他为中心来建立MST即可,花费就是(n-1)
。
给定了随机数的的范围是 L~R,并且数据范围在2e5内,在1e8范围内的质数两两之间最大的间距是666
(我自己试的,不是瞎写的),那么按理说,如果把随机数的范围拉满,只要1000个左右的数(n > 1000 200000 / 1000 = 200),就肯定会覆盖一个质数,那么我只要以它为中心建MST就好了,剩下的1000范围内暴力就好了
如果L == R
,答案就是(n-1)*L
Code
// Problem: Build Roads
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/15600/B
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
//
// Edited on 2021-08-06 15:34:57
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ull unsigned long long
#define ll long long
#define re return
#define Endl "\n"
#define pb push_back
#define endl "\n"
using namespace std;
typedef pair<int, int> PII;
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
const int N = 2e5 + 10;
ull n, L, R; // 必须用ull,爆ll了
ull seed;
ull a[N];
ull xorshift64(){
ull x = seed;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
return seed = x;
}
int gen(){
return xorshift64() % (R - L + 1) + L;
}
int cnt_e; // 边的个数
struct edge{
int u, v, w;
}e[2 * N];
bool cmp(edge a, edge b){
return a.w < b.w;
}
struct ufss{
int f[N];
void init(){
for(int i = 1; i <= 10000; i++){
f[i] = i;
}
}
int query(int p){
if(f[p] == p) return p;
else{
int v = query(f[p]);
f[p] = v;
return v;
}
}
void merge(int p1, int p2){
int f1 = query(p1);
int f2 = query(p2);
if(f1 != f2){
f[f1] = f2;
}
}
}ufs;
ll Kruskal(){
ll res = 0;
int cnt = 0;
ufs.init();
sort(e + 1, e + 1 + cnt_e, cmp);
for(int i = 1; i <= cnt_e; i++){
int u = ufs.query(e[i].u);
int v = ufs.query(e[i].v);
if(u == v) continue;
ufs.f[u] = v;
cnt++;
res += e[i].w;
}
if(cnt < n - 1) return -1; // 题目实际上保证一定有解,这里是复制的模版
else return res;
}
void init(){
for(int i = 1; i <= n; i++){
a[i] = gen();
}
for(int i = 1; i <= n; i++){
for(int j = i + 1; j <= n; j++){
e[++cnt_e].u = i; // 省赛的环境不能用结构体{}呜呜
e[cnt_e].v = j;
e[cnt_e].w = __gcd(a[i], a[j]);
}
}
}
int main(){
cin >> n >> L >> R >> seed;
if(L == R){
cout << L * (n - 1);
return 0;
}
if(n <= 1000){
init();
ll ans = Kruskal();
cout << ans;
}
else cout << n - 1;
}
凌乱之风大佬写的一种方法,妙啊!
// Problem: Build Roads
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/15600/B
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
//
// Edited on 2021-08-06 15:34:57
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define ull unsigned long long
#define pb push_back
using namespace std;
const int N = 2e5 + 10;
ull n, L, R; // 必须用ull,爆ll了
ull seed;
ull a[N];
ull xorshift64(){
ull x = seed;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
return seed = x;
}
int gen(){
return xorshift64() % (R - L + 1) + L;
}
int main(){
cin >> n >> L >> R >> seed;
if(L == R){
cout << L * (n - 1);
return 0;
}
if(n < 1000){
for(int i = 1; i <= n; i++){
a[i] = gen();
}
vector<ull> v;
for(int i = 1; i < n; i++){
for(int j = i + 1; j <= n; j++){
v.pb(__gcd(a[i], a[j]));
}
}
sort(v.begin(), v.end());
ull ans = 0;
for(int i = 0; i < n - 1; i++){ // 需要连
ans += v[i];
}
cout << ans;
}
else cout << n - 1;
}
C - Cat Virus
题意
给定一棵树,黑白染色方案,满足一个黑点的子树都是黑点,白点任意。 你现在构造一棵树,使得它的染色方案数为 K。
反思
看了各路大神的题解后拿笔模拟了一下,发现的确是有点巧妙地,还是得多根据样例多模拟才知道树是怎么构造出来的
Code
// Problem: Cat Virus
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/15600/C
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
//
// Edited on 2021-08-06 16:59:55
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"
using namespace std;
typedef pair<int, int> PII;
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
ll k;
int main(){
cin >> k;
ll fa = 1;
ll id = 1;
ll t = k;
ll cnt = 1;
while(t > 3){
if(t & 1){
t >>= 1;
cnt += 2;
}
else{
t--;
cnt++;
}
}
if(t == 3) cnt++;
cout << cnt << endl;
while(k > 3){
if(k & 1){
id ++;
cout << fa << " " << id << Endl;
id++;
cout << fa << " " << id << Endl;
fa = id;
k >>= 1;
}
else{
id++;
cout << fa << " " << id << Endl;
fa = id;
k--;
}
}
if(k == 3){
id ++;
cout << fa << " " << id << Endl;
}
}
D - Dyson Box
题意
给定n个方块,在两个重力的范围作用下,每放一个方块分别输出不同重力环境下的构成的整个方块的周长
题解
直接模拟就行
如果一个方块周围没有其他方块,那么对答案的贡献就是4
我们考虑每一种放方块的时候的周围的情况
- 在底部有方块的时候-1,并且相交原来的方块会使原来的答案-1
- 左右两侧有方块会使对答案的贡献 -1 ,并且相交原来的方块会使原来的答案-1
Code
// Problem: Dyson Box
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/15600/D
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
//
// Edited on 2021-08-06 20:58:29
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 2e5 + 10;
int n;
int shu[N];
int heng[N];
int ans_h;
int ans_s;
void f(int q[], int i, int& ans){
int tmp = 4;
if(q[i] >= 1){ // 底下有方块
tmp --;
ans --;
}
q[i] ++;
if(i > 1 && q[i - 1] >= q[i]){ // 左边有
tmp --;
ans--;
}
if(i < 2e5 && q[i + 1] >= q[i]){
tmp --;
ans --;
}
ans += tmp;
}
int main(){
cin >> n;
while(n--){
int x, y;
cin >> x >> y;
f(shu, x, ans_s);
f(heng, y, ans_h);
cout << ans_s << " " << ans_h << endl;
}
}
G - Grade Point Average
题意
求平均绩点
题解
模拟除法,保留k位
Code
// Problem: Grade Point Average
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/15600/G
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
//
// Edited on 2021-08-06 15:25:47
#include <iostream>
#include <cstdio>
using namespace std;
int n, k;
int main(){
cin >> n >> k;
int summ = 0;
for(int i = 1; i <= n; i++){
int x;
cin >> x;
summ += x;
}
cout << summ/n << ".";
summ %= n;
while(k--){
cout << summ * 10 / n;
summ *= 10;
summ %= n;
}
}
H - Adventurer’s Guild
其实看题意很明显的二维费用背包板子,但是我们还是要分析一下他的状态表示:
题意
有H点生命和S点体力,杀死一个怪物会消耗一定的生命和体力,会得到一定的报酬
生命值降为0会死,体力降为0后,还可以把多减的体力减到生命中去
题解
状态表示
f[i][j][k]表示从前 i 个怪物中选,消耗 j 点生命,k点体力获得的价值
属性
MAX
状态计算
注意
如果存好每一个属性,必须三维压二维;价值每个最多到1e9,需要开 ll
Code
// Problem: Adventurer's Guild
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/15600/H
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
//
// Powered by CP Editor (https://cpeditor.org)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
ll n, H, S;
ll h[1010];
ll s[1010];
ll w[1010];
ll f[310][310];
int main() {
cin >> n >> H >> S;
for(int i = 1; i <= n; i++) {
scanf("%lld%lld%lld", h + i, s + i, w + i);
}
ll ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = H; j > h[i]; j--) { // 不能取等于,题目要求她活着
for (int k = S; k >= 0; k--) {
if (k >= s[i]) {
f[j][k] = max(f[j][k], f[j - h[i]][k - s[i]] + w[i]);
}
else {
if (j - h[i] - (s[i] - k) > 0) // 得可以杀才能杀,前面的不用判断能不能杀,因为j的循环保证了
{
f[j][k] = max(f[j][k], f[j - h[i] - (s[i] - k)][0] + w[i]);
}
}
ans = max(ans, f[j][k]);
}
}
}
cout << ans << endl;
return 0;
}
M - Matrix Problem
题意
给定 01 矩阵 C,构造两个矩阵 A B,其中 形成了完整的不分散的一块四连通块,并且对于 C 中 所有位置,若是 1,则 A B 对应位置必须都是 1,否则 AB 之中必须有一个这个位置为 0(让1之间都连通,且两个矩阵不能完全相同)
题解
思维题
让其中一个矩阵的第一列全为1,另一个最优列全为1,行奇偶化为1
Code
不知道为什么现在在牛客上只能过90了,就连比赛中过的代码也过不了了,研究了好长时间,可能是数据变了
// Problem: Matrix Problem
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/15600/M
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// Code by: ING__
//
// Edited on 2021-08-06 22:04:34
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"
using namespace std;
const int N = 510;
int n, m;
int a[N][N];
int b[N][N];
char c[N][N];
int main(){
cin >> n >> m;
for(int i = 0; i < n; i++){
scanf("%s", c[i]);
}
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if(c[i][j] == '1') a[i][j] = 1;
if(i % 2 == 0 && j < m - 1) a[i][j] = 1;
if(!j) a[i][j] = 1;
}
}
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
cout << a[i][j];
}
cout << Endl;
}
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if(c[i][j] == '1') b[i][j] = 1;
if(i % 2 == 1 && j) b[i][j] = 1;
if(j == m - 1) b[i][j] = 1;
}
}
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
cout << b[i][j];
}
cout << Endl;
}
}