2020牛客暑期多校训练营(第八场)
第八场了,终于进了一次前500,贴个榜单纪念一下
G. Game SET
题目大意
定义一张牌有四个属性,分别为大小,形状,阴影和颜色,一个集合包含三张牌,并且每个集合中对于所有牌的某种属性,要么完全相同要么完全不同,问你在给定的 n n n张牌里面有没有满足条件的三张牌能够构成一个集合,若某张牌的某一属性为‘*’,说明该属性为任意值(俗称赖子)
解题思路
这题应该是这场真正的“签到题”,直接 O ( n 3 ) O(n^3) O(n3)的暴力就过了,不知道谁把榜给带歪了。。。
主要的做法就是从给定的 n n n张牌里面任选三张,暴力判断是否满足条件即可
AC代码
#include <bits/stdc++.h>
using namespace std;
string s[300][5];
bool judge(int i, int j, int k) {
bool flag = true;
for(int p = 1; p <= 4; ++p) {
if(s[i][p] == s[j][p] && s[j][p] == s[k][p]) continue;
if(s[i][p] != s[j][p] && s[i][p] != s[k][p] && s[j][p] != s[k][p]) continue;
if(s[i][p] == "*" && s[j][p] == s[k][p]) continue;
if(s[j][p] == "*" && s[i][p] == s[k][p]) continue;
if(s[k][p] == "*" && s[i][p] == s[j][p]) continue;
if(s[i][p] == "*" && s[j][p] == "*") continue;
if(s[j][p] == "*" && s[k][p] == "*") continue;
if(s[i][p] == "*" && s[k][p] == "*") continue;
flag = false;
break;
}
return flag;
}
int main() {
int T;
scanf("%d", &T);
int cas = 0;
while(T--) {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
string p;
cin >> p;
int len = p.length();
int cnt = 0, pre = 0;
for(int j = 0; j < len; ++j) {
if(p[j] == '[') pre = j;
else if(p[j] == ']') {
s[i][++cnt] = p.substr(pre + 1, j - pre - 1);
}
}
//cout << s[i][1] << " " << s[i][2] << " " << s[i][3] << " " << s[i][4] << '\n';
}
bool flag = false;
for(int i = 1; i <= n; ++i) {
for(int j = i + 1; j <= n; ++j) {
for(int k = j + 1; k <= n; ++k) {
if(judge(i, j, k)) {
flag = true;
printf("Case #%d: %d %d %d\n", ++cas, i, j, k);
break;
}
}
if(flag) break;
}
if(flag) break;
}
if(!flag) printf("Case #%d: -1\n", ++cas);
}
return 0;
}
K. Kabaleo Lite
题目大意
一家餐馆有 n n n种菜,每道菜有 a i a_{i} ai盘,每盘的收益为 b i b_{i} bi,每个客人都必须从第一盘菜开始点,并且其点菜顺序为 1 , 2 , 3... n 1,2,3...n 1,2,3...n,即点菜顺序必须连续,每道菜每个客人只能点一盘,问你在满足客人数最大的情况下所获得的的最大收益是多少
解题思路
首先考虑客人数最多,那肯定就是第一道菜的盘数,因为第一道菜点完后后面的菜就点不了了
其次考虑收益,由于点菜顺序必须连续,那么我肯定能点就点,也就是说我们将菜的收益的前缀和进行排序,按照前缀和的大小进行点菜
另外,假设第 i i i中菜已经点完了,那么 i i i以后的菜就不能点了,所以 a i = m i n ( a 1 , a 2 , . . . , a i ) a_{i}=min(a_{1},a_{2},...,a_{i}) ai=min(a1,a2,...,ai),按照前缀和排序后要注意菜被点完的情况
注:这题最大收益会把 l o n g l o n g long\ long long long爆掉,并且由于有负数的原因 u n s i g n e d l o n g l o n g unsigned\ long\ long unsigned long long不能用,只好用 _ _ i n t 128 \_\_int128 __int128代替了
AC代码
#include <iostream>
#include <sstream>
#include <vector>
#include <iomanip>
#include <string>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <set>
#include <math.h>
#include <stack>
#include <algorithm>
#include <queue>
#include <functional>
#include <ctime>
#include <list>
#include <cstring>
#define mod 1000000007
#define INF 0x3f3f3f3f
#define ll long long
#define ull unsigned long long
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS ios::sync_with_stdio(false),cout.tie(0)
using namespace std;
#define PI 3.1415926535898
using namespace std;
const int maxx = 1e5 + 5;
//int profit[maxx];
int dishs[maxx];
inline __int128 read() {
__int128 x = 0, f = 1;
char ch = getchar();
while (ch<'0' || ch>'9') {
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
inline void print(__int128 x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9)
print(x / 10);
putchar(x % 10 + '0');
}
struct node {
int id = 0;
__int128 pro = 0;
}no[maxx];
bool cmp(node a, node b) {
return a.pro < b.pro;
}
int main() {
int T;
scanf("%d", &T);
for (int i = 1; i <= T; ++i) {
int N;
__int128 res = 0;
scanf("%d", &N);
//int cnt = 0;
for (int j = 1; j <= N; ++j) {
no[j].id = j;
no[j].pro = no[j - 1].pro + read();
}
sort(no + 1, no + N + 1, cmp);
int minn = INF;
for (int j = 1; j <= N; ++j) {
scanf("%d", &dishs[j]);
if (dishs[j] > minn) {
dishs[j] = minn;
}
else {
minn = dishs[j];
}
}
printf("Case #%d: %d ", i, dishs[1]);
int minid = INF,cnt = 0;
for (int j = N; j >= 1 && dishs[1]-cnt > 0; --j) {
if (no[j].id < minid) {
res += no[j].pro * (dishs[no[j].id]-cnt);
minid = no[j].id;
cnt = dishs[no[j].id];
//dishs[1] -= dishs[no[j].id];
}
}
print(res);
printf("\n");
//printf("%lld\n", res);
}
}
I. Interesting Computer Game
题目大意
有 n n n次操作,每次你可以从给定的 a i a_{i} ai和 b i b_{i} bi中选择一个数,问你最后所选数字的种类数最大是多少
解题思路
你可以将 a i a_{i} ai和 b i b_{i} bi看做两个节点,并且这两个节点之间连了一条边,那么每次操作相当于在一条边上去除一个节点,模拟一下你会发现对于这个图的每个联通子图,如果子图中有环,那么这个子图中所有的节点都可以被选择,否则如果子图是一棵树,那么子图中可选节点数目为总结点数减一
那么这题的做法就是对于每个节点进行 d f s dfs dfs,若经过环,那么该节点所在子图对于答案的贡献为该子图的大小,否则为该子图大小减一
但是由于 a i , b i ≤ 1 e 9 a_{i},b_{i}\leq 1e^{9} ai,bi≤1e9,我们需要将其离散化到 2 N 2N 2N以内
AC代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int aaa[maxn];
int bbb[maxn];
int ccc[maxn];
bool vis[maxn];
vector<int> v[maxn];
int res = 0;
bool flag = false;
int dfs(int u, int fa) {
if (vis[u]) {
flag = true;
return 0;
}
vis[u] = true;
int len = v[u].size();
int res = 1;
for (int i = 0; i < len; ++i) {
if(v[u][i] != fa) res += dfs(v[u][i], u);
}
return res;
}
int main() {
int T;
scanf("%d", &T);
for (int i = 1; i <= T; ++i) {
int N;
scanf("%d", &N);
int cnt = 0;
for (int j = 1; j <= N; ++j) {
scanf("%d%d", &aaa[j], &bbb[j]);
ccc[++cnt] = aaa[j];
ccc[++cnt] = bbb[j];
}
sort(ccc + 1, ccc + cnt + 1);
int lenc = unique(ccc + 1, ccc + cnt + 1) - ccc - 1;
for (int j = 1; j <= lenc; ++j) {
v[j].clear();
vis[j] = false;
}
for (int j = 1; j <= N; ++j) {
aaa[j] = lower_bound(ccc + 1, ccc + lenc + 1, aaa[j]) - ccc;
bbb[j] = lower_bound(ccc + 1, ccc + lenc + 1, bbb[j]) - ccc;
v[aaa[j]].push_back(bbb[j]);
v[bbb[j]].push_back(aaa[j]);
}
res = 0;
for (int j = 1; j <= lenc; ++j) {
if (!vis[j] && v[j].size()) {
flag = false;
int size = dfs(j, 0);
res += flag ? size : size - 1;
}
}
printf("Case #%d: %d\n", i, res);
}
return 0;
}