补题地址:https://codeforces.com/gym/103055
本文按照通过率补的题
A. League of Legends
- 题意:两个队伍都是5个人,每个人有血量,每回合可以扣对方一点血,没血了输,蓝队先手,求谁会赢。
- 思路:直接求和判断一下谁更大,平局蓝队赢
#include<bits/stdc++.h>
using namespace std;
int main(){
int a = 0, b = 0;
for(int i = 1; i <= 5; i++){
int x; cin>>x; a += x;
}
for(int i = 1; i <= 5; i++){
int x; cin>>x; b += x;
}
if(a >= b)cout<<"Blue\n";
else cout<<"Red\n";
return 0;
}
C. Cube
- 题意:给你三个三维空间的八个点,检查它们是否可以组成一个立方体。(T<100)
- 思路:考虑每个点,从他出发与其他点所形成的边都有3条边相互垂直 且 相等。
或者枚举所有的边,数量满足12/12/4,且长度满足1:2:3,即可证明为立方体、
#include<bits/stdc++.h>
using namespace std;
int x[10], y[10], z[10];
int t[80], cnt;
int main(){
int T; cin>>T;
while(T--){
for(int i = 1; i <= 8; i++)cin>>x[i]>>y[i]>>z[i];
cnt = 0;
for(int i = 1; i <= 8; i++){
for(int j = i+1; j <= 8; j++){
t[++cnt] = (x[j]-x[i])*(x[j]-x[i])+(y[j]-y[i])*(y[j]-y[i])+(z[j]-z[i])*(z[j]-z[i]);
}
}
sort(t+1,t+cnt+1);
if(t[1]==t[12] && (t[1]!=0) && t[13]==t[24] && t[13]==2*t[1] && t[25]==t[28] && t[25]==3*t[1]){
cout<<"YES\n";
continue;
}
cout<<"NO\n";
}
return 0;
}
M. Game Theory
- 题意:老师和学生互相给对方打分(需要从自己有的分数里扣),范围在[1,20],假设老师打了x,学生打了y。如果x>y学生多给老师10分,反之老师多给学生10分。
问如果老师随机出值,每个学生可以自己选择值,面对n个学生的情况下(对每个学生都是独立情况)老师的期望得分是多少。 - 思路(详细版):
分数不会无故产生,都是老师和学生之间的流通,因此老师的期望得分就是学生的期望得分的负数。
对任意一轮,设老师出的值是x,学生是y。那么
老师赢:−x+y+10。x∈[y+1,20]时
老师负:−x+y−10。x∈[1,y−1]时
平局:−x+y=0。x=y时
期望E = 期望得分*对应概率 =(210−19x−y)/20,因此学生的期望就是-E。
因为学生可以自己决定y的大小,于是y一定是出最大为最优,老师是等概率出数字的,于是出的x的期望一定是10,带入E=0。 - 思路(简略版)
两人策略完全对称,胜负对等,期望为0。
直接输出0即可
至于输入,大家互相独立,班里有多少人并不重要
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<0<<"\n";
return 0;
}
J. Grammy and Jewelry
- 题意:给你一张图,每个点上面都有无穷个宝石,每个宝石都有自己的价值,同一个点上的宝石价值一样。边权为1,每次可以从点1出发去某个点拿走一个宝石然后回到1号点, 当且仅当你去那个点拿到一个宝石并且回到起点才算得到这个宝石的价值, 手上最多只能有一个宝石。每次经过一条边的耗时是1,问在时间T内的每个时间点可以得到的价值最多是多少。
- 思路:
因为每次都是1号点出发回到1号点,且只能拿1个宝石,所以不难bfs预处理出拿到任何一个宝石的代价。
考虑到题目说每个宝石都有自己特定的价值,且可以无限拿,那么就是个完全背包了,套板子即可。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0), cin.tie(0),cout.tie(0)
const int maxn = 1e5+10;
int v[maxn], w[maxn];//每个物品的价值,重量
vector<int>G[maxn];
void bfs(){
queue<pair<int,int> >q;
q.push({1,0});
while(q.size()){
auto x = q.front(); q.pop();
int u = x.first, dis = x.second;
for(int nu : G[u]){
if(w[nu])continue;
w[nu] = 2*(dis+1);
q.push({nu, dis+1});
}
}
}
int f[maxn];
int main(){
IOS;
int n, m, T; cin>>n>>m>>T;
for(int i = 2; i <= n; i++)cin>>v[i];
for(int i = 1; i <= m; i++){
int x, y; cin>>x>>y;
G[x].push_back(y);
G[y].push_back(x);
}
bfs();
for(int i = 1; i <= n; i++){
for(int j = w[i]; j <= T; j++){//T是总重量
f[j] = max(f[j], f[j-w[i]]+v[i]);
}
}
for(int i = 1; i <= T; i++)cout<<f[i]<<" ";
return 0;
}
L. String Freshman
- 题意:给出一个KMP的假算法,问字符串T在匹配的时候是否会存在一个字符串S使得答案错误。
- 思路:KMP是如果不匹配就跳到next数组中,而给出的代码是直接跳到起点,所以如果我们的next数组中有一个next值不是跳到起点的,就可以构造出这样的字符串把他hack掉。
因此只要判断是否有字符和第一个字符相等即可,如果存在相等就是WA。
#include<bits/stdc++.h>
using namespace std;
int main(){
int n; cin>>n;
string s; cin>>s;
for(int i = 1; i < n; i++){
if(s[i]==s[0]){
cout<<"Wrong Answer\n";
return 0;
}
}
cout<<"Correct\n";
return 0;
}
G. Wall Game
- 题意:
n组输入,每组输入三个数字,第一个数字代表操作类型, 第二三个数字代表坐标。
操作1:在整个图上添加点,并和相邻的点联通
操作2:查询这个点的整个联通的快,输出整个块的边的数量 - 思路:
对于图上加点,可以给每个点的坐标离散化成序号,然后丢到并查集维护连通性。
对于输出联通块边的数量,维护一下每个集合内点的数量和重复边的数量,*6-v输出即可。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0), cin.tie(0),cout.tie(0)
const int maxn = 5e5+10;
//并查集
int fa[maxn+10];
int num[maxn+10], rpp[maxn]; //集合大小,集合内重边数
void init(int n){for(int i = 0; i <= n; i++)fa[i]=i;}
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void merge(int x, int y){x=find(x);y=find(y);if(x!=y)fa[x]=y;}
//离散化
struct node{
int x, y;
node(){} node(int a, int b) : x(a), y(b){}
bool operator < (node const b) const { return x==b.x ? y < b.y : x < b.x;; }
}a[maxn];int cnt=0;
map<node, int>mp;
int dx[] = {1,1,0,0,-1,-1};
int dy[] = {0,-1,-1,1,0,1};
int main(){
IOS;
int T; cin>>T;
init(T+1);
while(T--){
int op, x, y; cin>>op>>x>>y;
if(op==1){
a[++cnt] = (node){x,y};
mp[(node){x,y}] = cnt;
num[cnt] = 1;
rpp[cnt] = 0;
for(int i = 0; i < 6; i++){ //每次新建后考虑连接6个方向
int nx = x+dx[i], ny = y+dy[i];
if(!mp.count((node){nx,ny})){
continue;
}
int old = find(mp[(node){nx,ny}]); //都合并到当前坐标
if(old != cnt){
fa[old] = cnt;
rpp[cnt] += rpp[old]+2;
num[cnt] += num[old];
}else{
rpp[cnt] += 2;
}
}
}else{
node t = (node){x,y};
if(!mp.count(t))cout<<6<<"\n";
else{
int x = find(mp[t]);
cout<<num[x]*6-rpp[x]<<"\n";
}
}
}
return 0;
}
F. Fair Distribution
- 题意:T组数据,每组数据有n, m两个数,其中n只能减,m只能加,每次加一或者减一的代价是1,问使最后m % n == 0的代价最小是多少。
- 思路:
对于n>m的情况,答案显然为n − m(因为m%n == m,只能把m加到跟n一样大才行)
对于m<n的情况
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
int T; cin>>T;
while(T--){
LL n, m; cin>>n>>m;
if(n > m)cout<<n-m<<"\n";
else{
LL ans = 1e9+10;
for(LL l = 1, r; l <= n; l = r+1){
r = min(n, (m-1)/((m-1)/l));
ans = min(ans, (m-1)/l*l);
}
cout<<ans+n-m<<"\n";
}
}
return 0;
}