训练实录 | 2021牛客暑期多校训练营2

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题同样)
1

#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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值