2021牛客暑期多校训练营2
C - Draw Grids
solved by oye. 00:22:07(+)
题意:
给n,m两个数,表示有n行点和m列点,已知每次只能连两个相邻的点(斜的不能连),连的线和已有的线不能组成封闭图形。如果无法连线就输了,问ZYT先手时,对于给定的点数,是赢还是输。
因为n,m
≤
\leq
≤ 4,就十种可能,所以直接列。
#include<iostream>
#include<string.h>
#include<queue>
#include<stack>
#include<math.h>
#include<map>
#include<set>
#include<algorithm>
#include<sstream>
#include<vector>
#include<ctype.h>
#include<deque>
using namespace std;
#define ll long long
int main(){
int n,m;
cin>>n>>m;
if(n>m)swap(n,m);
if((n==1&&(m==1||m==3))||(n==3&&m==3))printf("NO");
else printf("YES");
}
D - Er Ba Game
solved by Yuki Sam. 00:45:14(-2)
题意: 一种游戏,甲乙各拿一张牌
(
a
1
,
b
1
)
(a1,b1)
(a1,b1) 和
(
a
2
,
b
2
)
(a2,b2)
(a2,b2) 。规则如下:
①一旦有人出现(2,8)的牌,直接获胜。(如果两个人都有则平局 )
②如果无(2,8),但是甲的牌满足
a
=
b
a=b
a=b ,
——if 乙的牌满足
a
≠
b
a≠b
a=b ----->甲赢
——else if 乙的牌也满足
a
=
b
a=b
a=b ----->比较两者的a,谁大谁赢,一样大则平局
③如果两人的牌都是
a
≠
b
a≠b
a=b ,
比较
(
a
+
b
)
(a+b)
(a+b) %10 的大小,谁大谁赢
——if 相等,比较两者的b,谁大谁赢,一样大则平局
wa两发,第一次是因为没考虑两个都(2,8)的平局情况(要理清楚什么时候会平局),第二次发现swap的时候写错角标的,白给两发罚时,下次写完要检查一下代码,不能盲交。
#include<bits/stdc++.h>
using namespace std;
int T;
int a1,b1,a2,b2;
int main()
{
cin>>T;
while(T--)
{
cin>>a1>>b1>>a2>>b2;
if(a1>b1) swap(a1,b1);
if(a2>b2) swap(a2,b2);
if(a1==2&&b1==8)
{
if(a2==2&&b2==8) printf("tie\n");
else printf("first\n");
}
else if(a2==2&&b2==8) printf("second\n");
else if(a1==b1&&a2!=b2) printf("first\n");
else if(a2==b2&&a1!=b1) printf("second\n");
else if(a1==b1&&a2==b2)
{
if(a1>a2) printf("first\n");
else if(a1<a2) printf("second\n");
else printf("tie\n");
}
else if(a1!=b1&&a2!=b2)
{
int x=(a1+b1)%10,y=(a2+b2)%10;
if(x>y) printf("first\n");
else if(x<y) printf("second\n");
else
{
if(b1>b2) printf("first\n");
else if(b1<b2) printf("second\n");
else printf("tie\n");
}
}
}
return 0;
}
F - Girlfriend
solved by Yuki Sam. 04:56:13(-5)
题意: t组数据,每组给出A、B、C、D四个定点的坐标,两个系数 k 1 k_1 k1 、 k 2 k_2 k2 ,三维空间,现有两个动点P1、P2,满足 P 1 A P 1 B \frac{P_1A}{P_1B} P1BP1A ≥ k 1 ≥k_1 ≥k1 , P 2 C P 2 D \frac{P_2C}{P_2D} P2DP2C ≥ k 2 ≥k_2 ≥k2 , 求满足条件的动点 P 1 P_1 P1 形成的几何体与动点 P 2 P_2 P2 形成的几何体,两者的体积交。
思路: 根据题意显然可知是利用圆的第二定义,在二维平面上动点所形成的图形即为阿波罗尼斯圆,现在无非是将其从二维推向三维。以动点
P
1
P_1
P1 形成的几何体为例,连接AB,直线AB可以存在于无数个平面中,所以可以任选一平面,画出阿波罗尼斯圆,再以AB为轴转一圈,形成的球体即为我们所求几何体,动点
P
2
P_2
P2 同理。
所以题目就简化成:求两个阿波罗尼斯球的体积交。只要确定两球的球心和半径即可。
wa了很多发,一个小时的时候开了这题,觉得可实现性挺大,就开始敲,半小时敲完交了一发wa了觉得蒙了,因为觉得自己思路肯定没错。后来坐牢一小时debug想不出哪里不对,甚至重推一遍,破防到差点想提前下班,(甚至中途还吃了个wyf的瓜?dbq)。最后半小时的时候,抱着破罐子破摔的心态再看一遍,重新读题,发现大问题,我把误差 控制在1e-3内理解成了保留三位小数位数 。本题教训:好好学英语,好好读题,发现不对先重读一遍题目。(下面K题同样)
#include <bits/stdc++.h>
using namespace std;
const double pi = acos((double)(-1));
#define inf 0x3f3f3f3f
#define ll long long
#define eps 1e-8
int sgn(double x) //判断x是否为0
{
if(fabs(x) < eps) return 0;
else return x < 0?-1:1;
}
//球的结构体
struct ball
{
double x,y,z,r;
ball(double x=0,double y=0,double z=0,double r=0):x(x),y(y),z(z),r(r){}
double dist(ball a){
return sqrt((x-a.x)*(x-a.x)+(y-a.y)*(y-a.y)+(z-a.z)*(z-a.z));
}
double v(){
return pi*r*r*r*4/3;
}
}o1,o2;
//求球的体积并
double solve(ball a,ball b)
{
double d = a.dist(b);
double r1=a.r,r2=b.r;
double v1=pi/3*(r1-(r1*r1-r2*r2+d*d)/(2*d))*(r1-(r1*r1-r2*r2+d*d)/(2*d));
v1 *= 3*r1- (r1-(r1*r1-r2*r2+d*d)/(2*d));
double v2=pi/3*(r2-(r2*r2-r1*r1+d*d)/(2*d))*(r2-(r2*r2-r1*r1+d*d)/(2*d));
v2 *= 3*r2- (r2-(r2*r2-r1*r1+d*d)/(2*d));
return (v1+v2);
}
//点的结构体
struct point
{
double x,y,z;
point(){}
point(double x,double y,double z):x(x),y(y),z(z){}
point operator + (point B){return point(x + B.x,y + B.y,z+B.z);}
point operator - (point B){return point(x - B.x,y - B.y,z-B.z);}
point operator * (double k){return point(x*k,y*k,z*k);}
point operator / (double k){return point(x/k,y/k,z/k);}
bool operator == (point B){return sgn(x - B.x) == 0 && sgn(y - B.y) && sgn(z-B.z) == 0;}
}a,b,c,d,p_1,p_2,p_3,p_4,o_1,o_2;
double dis(point A,point B) //空间两点距离
{ return sqrt((A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y)+(A.z - B.z)*(A.z - B.z)); }
int t;
double k1,k2;
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%lf%lf%lf",&a.x,&a.y,&a.z);
scanf("%lf%lf%lf",&b.x,&b.y,&b.z);
scanf("%lf%lf%lf",&c.x,&c.y,&c.z);
scanf("%lf%lf%lf",&d.x,&d.y,&d.z);
scanf("%lf%lf",&k1,&k2);
//求动点P1形成的球体o1
p_1=(a-b)*1.0/(k1+1)+b;
p_2=(b-a)*1.0/(k1-1)+b;
o_1=(p_1+p_2)/2;
o1.r=dis(p_1,p_2)/2.0;
o1.x=o_1.x , o1.y=o_1.y , o1.z=o_1.z;
//求动点P2形成你的球体o2
p_3=(c-d)*1.0/(k2+1)+d;
p_4=(d-c)*1.0/(k2-1)+d;
o_2=(p_3+p_4)/2;
o2.r=dis(p_3,p_4)/2.0;
o2.x=o_2.x , o2.y=o_2.y , o2.z=o_2.z;
double diso = dis(o_1,o_2) , minr = min(o1.r,o2.r) , minv = pi*minr*minr*minr*4.0/3;
//分类讨论 相离/切 内含/切 相交
if(diso>=o1.r+o2.r) printf("0\n");
else if(diso<=fabs(o1.r-o2.r)) printf("%lf\n",minv);
else printf("%lf\n",solve(o1,o2));
}
return 0;
}
K - Stack
solved by Micky&&oye. (after)
题意:有一个数组a,将a中的元素放到单调栈中,给出几个数据表示a放到第i个时栈的大小。求出一个这样的a数组。
看到题解后才知道这题原来可以用拓扑排序做(我是废物)。如果要拓扑排序的话,自然就会想到要去确定a数组中每个数之间的大小关系。确定完大小关系后直接赋值就ok了。
确定大小关系的话,可以通过数组b来确定。遍历一遍n,如果发现b[i]比栈元素+1要大直接输出-1,return 0。如果b[i]=0,说明第i个元素比栈顶的元素要小,如果b[i]比栈中的元素个数+1要小,那么说明这之前弹出过一些数。那么第i个元素就应该比放入i时,将所有元素弹出后栈的栈顶元素要大。由以上的大小关系的判断比i大的元素j即to[i] = j。然后对于to数组。是一条i->j的边。可得j的入度++。
入度遍历完后拓扑排序就行了。——Micky
#include<iostream>
#include<string.h>
#include<queue>
#include<stack>
#include<math.h>
#include<map>
#include<set>
#include<algorithm>
#include<sstream>
#include<vector>
#include<ctype.h>
#include<deque>
#pragma warning(disable:4996)
#include<functional>
using namespace std;
#define ll long long
#include<list>
#include <unordered_map>
#define ll long long
#define ull unsigned long long
#define PP pair<int,pair<double ,double>>
#define P pair<int,int>
#define limit 5000
#pragma warning(disable:4996)
using namespace std;
const double pi = acos((double)(-1));
#define inf 0x3f3f3f3f
#define ll long long
#define eps 1e-8
const int maxn = 1e6 + 9;
using i64 = long long;
int a[maxn], in[maxn], ans[maxn], b[maxn], to[maxn], st[maxn];
queue<int>q;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, k;
cin >> n >> k;
for (int i = 1; i <= k; i++) {
int x, y;
cin >> x >> y;
b[x] = y;
}
int instack = 0, flag = 0;
for (int i = 1; i <= n; i++) {
if (b[i]) {
if (b[i] > instack + 1) { // 在i的时候实际在栈里的元素小于本应该在栈里的元素。
cout << -1;
return 0;
}
flag = 0;
while (b[i] < instack + 1) {
instack--;
flag = 1;
}
//加入第i个元素后如果在实际栈里的元素大于应该在栈里的元素,说明栈中有元素弹出,故进行模拟弹出操作。
if (flag) {
to[st[instack + 1]] = i;
}
//由于弹出的元素比第i个元素都要大,而被弹出的最后一个元素时最小的,所以建立被弹出的最后一个元素指向第i个元素的边,说明大于关系
}
st[++instack] = i; //向栈中加入第i个元素。
to[st[instack]] = st[instack - 1]; //当向栈中加入一个元素后,加入的这个元素一定比原来栈顶的元素大,故将该加入的元素建立一条指向原来栈顶的元素的边,表示大于关系。
}
//上述的to数组表示的是,下标元素到该数组的值所代表的元素有一条有向边。表示大于关系,如to[i]=j表示第i个元素大于第j个元素
for (int i = 1; i <= n; i++) {
in[to[i]]++; //存在关系 i->to[i] 故to[i]的入度++。
}
//入度建立完毕后拓扑排序就好了
for (int i = 1; i <= n; i++) {
if (in[i] == 0) {
q.push(i);
}
}
int num = n;
while (!q.empty()) {
int temp = q.front();
q.pop();
a[temp] = num--;
in[to[temp]]--;
if (in[to[temp]] == 0)q.push(to[temp]);
}
for (int i = 1; i <= n; i++) {
cout << a[i] << ' ';
}
return 0;
}
做法二:这个做法应该跟拓扑没什么关系(主要我不是很会拓扑,太菜了我)
简单来说,这个做法就是构造一个b数组。我们对题目给定的b数组进行读入,顺带用一个数组把他们的下标标记一下,中途还可以判断一下b数组的值是否大于下标,大于的话,后续就不用构造了,直接可以出-1了。
读入完成后,开始构建b数组。先从左往右找被标记了的b,然后从这个点往左开始对下一个b赋值,赋的值为前一个的值减一(因为往右看的话,合理的数组应该是加一
我
们
是
倒
着
赋
值
的
,
倒
着
就
是
减
一
_{我们是倒着赋值的,倒着就是减一}
我们是倒着赋值的,倒着就是减一或者减很多或者不变,为了使上一个标记点和构造出来的b连续起来,所以用加一)一直赋到值为1或者上一个被标记点。
然后再从左到右把没赋值过的b赋值一下,为了简便起见,赋给未赋值的b他前一项的值(加减都不是很方便,有可能会出现0)
接着对合理性进行判断,合理的数组应该是使b[1]=1,并且后一项减前一项不能大于1(因为增加是只能增加1的)
如果数组合理,就可以填a数组的值了。
对于1来讲,他是最小的数,所以应该出现在b=1并且b的下标最大的那个位置(如果不是下标最大,譬如第二大的话,最大的下标那的数怎么也不可能小于1,所以不可能使b=1)
对2,3……同理
对于这填a我比较喜欢拿优先队列搞,因为要把b按值从小到大,值相同要按下标从大到小排,我开的又是数组,所以优先队列比较方便(虽然我现在还不会拼优先的单词=。=)
思路是参照牛客那里的题解讨论来的,那里讲得可能更清楚一点——这里是菜鸡的oye
#include<iostream>
#include<string.h>
#include<queue>
#include<stack>
#include<math.h>
#include<map>
#include<set>
#include<algorithm>
#include<sstream>
#include<vector>
#include<ctype.h>
#include<deque>
using namespace std;
#define ll long long
const int maxn=1e6+10;
int a[maxn];
int b[maxn];
int vis[maxn];
int main(){
int n,k;
cin>>n>>k;
memset(b,0,sizeof b);
memset(vis,0,sizeof vis);
int f=1;
for(int i=0;i<k;i++){
int x,y;
scanf("%d %d",&x,&y);
b[x]=y;
if(x<y) f=0;
vis[x]=1;
}
if(f){
for(int i=1;i<=n;i++){
if(vis[i]){
for(int j=0;j<b[i]&&!vis[i-j-1] && i-j-1>=1;j++){
b[i-j-1]=b[i]-j-1;
}
}
}
b[0]=1;
for(int i=1;i<=n;i++){
if(!b[i]){
b[i]=b[i-1];
}
}
for(int i=1;i<=n;i++){
if(b[1]!=1||b[i]-b[i-1]>1){
f=0;break;
}
}
}
if(f){
priority_queue<pair<int,int> >q;
for(int i=1;i<=n;i++){
q.push(make_pair(-b[i],i));
}
int top=1;
while(!q.empty()){
int x=q.top().second;
q.pop();
a[x]=top++;
}
for(int i=1;i<=n;i++){
if(f==1) printf(" ");
printf("%d",a[i]);
}
}else printf("-1");
}
I - Penguins
solved by Micky. (after)
题意:两只企鹅,两个20*20的图。一只企鹅在左图的(20,20)起点,终点为(1,20)。另一只在右图的起点(20,1),终点(1,1)。两只企鹅每次可以各走一格。两只企鹅的移动是镜像的,即一个向左,另一个向右。上下走没影响。
注意点:一只企鹅被挡住必须不动。没被挡住一定要动。每个数据中一定存在一条路径使得他们两个企鹅都能达到终点。
思路
最短路:使用广度优先搜索,从起点开始扩散,那条路先到,就是最短路。
路径记录:每一条路径都对应一个结构体,结构体里面有一个字符串s用来记录路径。
字典序最小:只要按照DLRU的顺序遍历方向就可以满足字典序最小。
#pragma warning(disable:4996)
#include<iostream>
#include<string.h>
#include<queue>
#include<stack>
#include<math.h>
#include<map>
#include<set>
#include<algorithm>
#include<sstream>
#include<vector>
#include<ctype.h>
#include<list>
#include <unordered_map>
#include<deque>
#include<functional>
using namespace std;
#define ll long long
#define ll long long
#define ull unsigned long long
#define PP pair<int,pair<double ,double>>
#define P pair<int,int>
#define limit 5000
#define inf 0x3f3f3f3f
#define eps 1e-8
const double pi = acos((double)(-1));
const int maxn = 1e6 + 9;
using i64 = long long;
int leftgo[4][2] = { {1,0}, {0,-1}, {0,1}, {-1,0} };
int rightgo[4][2] = { {1,0}, {0,1}, {0,-1}, {-1,0} };
char go[5] = { "DLRU" };
bool vis[22][22][22][22];
bool check(int x, int y) {
return x <= 20 && x >= 1 && y <= 20 && y >= 1;
}
struct info {
int x1, y1, x2, y2, step = 0;
string s = "";
};
queue<info>q;
char map1[25][25], map2[25][25];
char change(char x) {
if (x == 'L')return 'R';
if (x == 'R')return 'L';
return x;
}
void setthepath(string s) {
int mean_of_dig[222][2];
mean_of_dig['D'][0] = 1;
mean_of_dig['D'][1] = 0;
mean_of_dig['L'][0] = 0;
mean_of_dig['L'][1] = -1;
mean_of_dig['R'][0] = 0;
mean_of_dig['R'][1] = 1;
mean_of_dig['U'][0] = -1;
mean_of_dig['U'][1] = 0;
int lx = 20, ly = 20, rx = 20, ry = 1;
map1[lx][ly] = map2[rx][ry] = 'A';
for (int i = 0; i < s.size(); i++) {
int ldx = lx + mean_of_dig[s[i]][0], ldy = ly + mean_of_dig[s[i]][1];
int rdx = rx + mean_of_dig[change(s[i])][0], rdy = ry + mean_of_dig[change(s[i])][1];
if (check(ldx, ldy) && map1[ldx][ldy] != '#') {
lx = ldx;
ly = ldy;
}
if (check(rdx, rdy) && map2[rdx][rdy] != '#') {
rx = rdx;
ry = rdy;
}
map1[lx][ly] = map2[rx][ry] = 'A';
}
}
int main() {
for (int i = 1; i <= 20; i++) {
for (int j = 1; j <= 20; j++) {
cin >> map1[i][j];
}
for (int j = 1; j <= 20; j++) {
cin >> map2[i][j];
}
}
info temp = { 20,20,20,1,0,"" };
q.push(temp);
vis[20][20][20][1] = 1;
while (!q.empty()) {
info u = q.front();
q.pop();
if (u.x1 == 1 && u.y1 == 20 && u.x2 == 1 && u.y2 == 1) {
setthepath(u.s);
cout << u.step << endl;
cout << u.s << endl;
for (int i = 1; i <= 20; i++) {
printf("%s ", map1[i] + 1);
printf("%s\n", map2[i] + 1);
}
return 0;
}
for (int i = 0; i < 4; i++) {
int dx1 = u.x1 + leftgo[i][0], dy1 = u.y1 + leftgo[i][1];
int dx2 = u.x2 + rightgo[i][0], dy2 = u.y2 + rightgo[i][1];
int x1 = u.x1, y1 = u.y1, x2 = u.x2, y2 = u.y2;
if (check(dx1, dy1) && map1[dx1][dy1] != '#') {
x1 = dx1;
y1 = dy1;
}
if (check(dx2, dy2) && map2[dx2][dy2] != '#') {
x2 = dx2;
y2 = dy2;
}
if (vis[x1][y1][x2][y2])continue;
vis[x1][y1][x2][y2] = 1;
q.push({ x1,y1,x2,y2,u.step + 1, u.s + go[i] });
}
}
return 0;
}