2021牛客暑假多校 【k题Stack】【F题Girlfriend】

本文解析了一道涉及空间几何体问题的算法,通过理解球体、球壳及空间点的变换关系,探讨如何利用已知条件求解满足特定比例的点集所构成几何体的体积交。核心算法包括球的体积计算和球心距离判断,适合对空间几何及程序设计感兴趣者。
摘要由CSDN通过智能技术生成

K Stack

题意:
有一个排列a,经过如下操作(伪代码)变成了数列b,告诉你b的部分,让你还原排列a。

Stk is an empty stack
for i = 1 to n :
    while ( Stk is not empty ) and ( Stk's top > a[i] ) : 
        pop Stk
    push a[i]
    b[i]=Stk's size

输入描述:第一行两个数n,k代表排列a的长度,k代表接下来又k行。接下来的k行,每行输入两个数pi和xi,表示b(pi) = xi。
输出:输出排列a,无解输出-1
在这里插入图片描述
分析:
一、
搞清楚什么是排列,线性代数好像学过
二、
注意到为代码中的循环体内,可分为3句话,第一句话是个while循环,是个循环可以执行好多次,第二句话是一个push操作,没有任何限制,对于任意一个ai都会执行,第三句话是由栈的大小确定数列b的值
三、
由于输入给的只是b的一部分,b数组完整的样子可以是很多中,根据伪代码我们可知bi与bi-1的关系,首先,据2中分析,push操作一定执行,则bi可以等于b(i-1) + 1,若执行了while循环,则bi可以等于bi-1,也可以小于bi-1。而这个题明显由多解的样子,再根据上面的分析,对于一个未由输入给得的bi,让其等于b(i-1) + 1是一定可以的,由此,可以把整个b数组给搞出来。
四、
由b数组推出满足条件的a排列。首先是逆着推回去的,先推出an再推出an-1是合理的(避免a排列之间有影响),所以需要对b数组从n到1遍历,每次循环首先需要满足bi与栈的大小相同(对应伪代码中b[i]=Stk’s size),满足这个条件之后,栈顶元素就是ai(对应伪代码push a[i]),最后pop栈顶,因为对于i-1的情况是栈顶的元素是没有的。
AC代码

#include <bits/stdc++.h>

using namespace std;

int ar[1000005];
int br[1000005];
stack<int> st;
int n, k, cnt, x, y;

int main()
{
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= k; ++i)
    {
        scanf("%d%d", &x, &y);
        br[x] = y;
    }
    for(int i = 1; i <= n; ++i)
    {
        if(br[i] == 0) br[i] = br[i - 1] + 1;
        else if(br[i] > br[i - 1] + 1)
        {
            printf("-1\n");
            return 0;
        }
    }
    for(int i = n; i >= 1; --i)
    {
        while(br[i] > st.size()) st.push(++cnt);
        ar[i] = st.top();
        st.pop();
    }
    for(int i = 1; i <= n; ++i) printf("%d ", ar[i]);
    return 0;
}

F Girlfriend

题意:
先输入一个t表示样例组数,每组样例输入有5行,前4行每行3个数分别代表点a,b,c,d的x,y,z坐标,第五行两个数代表k1和k2,求满足下面式子的点p1和点p2所构成的空间几何体的体积交。
在这里插入图片描述
分析:
首先,调用高中知识,对于空间中,满足|AP1| = K|BP1|的点p1的轨迹是一个球壳,如果是>=k的话,即放大k的话仍是一个球壳且球心位置不变,变化的是球的半径,球的半径是变小了的,因此满足|AP1|>=K1|BP1|的点p1的轨迹实际是一个球体,这个球的球心与半径和满足|AP1| = K|BP1|的点p1所构成的球壳的球心和半径是一样的。
然后有一个板子,用处是求两个球的体积交,需要知道两个球的球心及半径,这个根据上面的式子化简再根据球的方程的知识可以得到。

一个比较潦草的手算推导过程:
在这里插入图片描述
两球体积交的板子:

//板子:求两球的体积交,8个参数表示两个球的半径及球心坐标
//最后的结果是在ans里,直接print了
const double pi = acos(-1);
void dio(double x1,double y1,double z1,double r1,double x2,double y2,double z2,double r2){
    double ans=0;
    //球心距离
    double dis=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2));
    //相离或相切
    if(dis>=r1+r2){
        ans=0;
    }
    //内含或内切
    else if (dis+r1<=r2){
        ans=(4.00/3.00)*pi*r1*r1*r1;
    }
    else if(dis+r2<=r1){
        ans=(4.00/3.00)*pi*r2*r2*r2;
    }
    //相交
    else{
        double cal=(r1*r1+dis*dis-r2*r2)/(2.00*dis*r1);
        double h=r1*(1-cal);
        ans+=(1.00/3.00)*pi*(3.00*r1-h)*h*h;
        cal=(r2*r2+dis*dis-r1*r1)/(2.00*dis*r2);
        h=r2*(1.00-cal);
        ans+=(1.00/3.00)*pi*(3.00*r2-h)*h*h;
    }
    printf("%.3f\n",ans);
}

AC代码:

#include <bits/stdc++.h>

using namespace std;

double x[10], y[10], z[10];
int t;
double k1, k2;
double x1, yy1, z1, x2, y2, z2, r1, r2;
double chu1, chu2, d1, d2;

//板子:求两球的体积交,8个参数表示两个球的半径及球心坐标
//最后的结果是在ans里,直接print了
const double pi = acos(-1);
void dio(double x1,double y1,double z1,double r1,double x2,double y2,double z2,double r2){
    double ans=0;
    //球心距离
    double dis=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2));
    //相离或相切
    if(dis>=r1+r2){
        ans=0;
    }
    //内含或内切
    else if (dis+r1<=r2){
        ans=(4.00/3.00)*pi*r1*r1*r1;
    }
    else if(dis+r2<=r1){
        ans=(4.00/3.00)*pi*r2*r2*r2;
    }
    //相交
    else{
        double cal=(r1*r1+dis*dis-r2*r2)/(2.00*dis*r1);
        double h=r1*(1-cal);
        ans+=(1.00/3.00)*pi*(3.00*r1-h)*h*h;
        cal=(r2*r2+dis*dis-r1*r1)/(2.00*dis*r2);
        h=r2*(1.00-cal);
        ans+=(1.00/3.00)*pi*(3.00*r2-h)*h*h;
    }
    printf("%.3f\n",ans);
}

int main()
{
    scanf("%d", &t);
    while(t--)
    {
        for(int i = 1; i <= 4; ++i)  scanf("%lf%lf%lf", &x[i], &y[i], &z[i]);
        scanf("%lf%lf", &k1, &k2);

        //cout << 10 << '\n';

        chu1 = k1 * k1 - 1;
        x1 = (k1 * k1 * x[2] - x[1]) / chu1;
        yy1 = (k1 * k1 * y[2] - y[1]) / chu1;
        z1 = (k1 * k1 * z[2] - z[1]) / chu1;
        d1 = (k1 * k1 * (x[2] * x[2] + y[2] * y[2] + z[2] * z[2]) - (x[1] * x[1] + y[1] * y[1] + z[1] * z[1])) / chu1;
        r1 = sqrt(x1 * x1 + yy1 * yy1 + z1 * z1 - d1);

        chu2 = k2 * k2 - 1;
        x2 = (k2 * k2 * x[4] - x[3]) / chu2;
        y2 = (k2 * k2 * y[4] - y[3]) / chu2;
        z2 = (k2 * k2 * z[4] - z[3]) / chu2;
        d2 = (k2 * k2 * (x[4] * x[4] + y[4] * y[4] + z[4] * z[4]) - (x[3] * x[3] + y[3] * y[3] + z[3] * z[3])) / chu2;
        r2 = sqrt(x2 * x2 + y2 * y2 + z2 * z2 - d2);

        //cout << 5 << '\n';
        dio(x1, yy1, z1, r1, x2, y2, z2, r2);
    }
    return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值