2021年度热身训练赛第一场

2021年度热身训练赛第一场

A.Weird Flecks, But OK

题意
给定n个点以及n个点的三维坐标,用一根长度不限的圆柱以垂直x—y平面,x—z平面或y—z平面方式穿过所有点,求圆柱的最小直径。
思路
例如圆柱垂直x—y平面时,可等价于将所有点的z坐标变为0,将问题转化成x—y平面上最小覆盖圆问题。同理重复三次并取min即可得到答案。
代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define pb push_back
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 3e5 + 19;
const ll mod = 1e9 + 7;
#define eps 1e-8
const int maxn = 100000+5;
int sgn(double x)
{
    if (fabs(x)<eps)
        return 0;
    else
        return x<0? -1:1;
}
struct Point
{
    double x,y;
};
double Distance(Point A, Point B)
{
    return hypot(A.x-B.x,A.y-B.y);
}
//求三角形abc的外接圆圆心
Point circle_center(const Point a, const Point b, const Point c)
{
    Point center;
    double a1 = b.x-a.x,b1=b.y-a.y,c1=(a1*a1+b1*b1)/2;
    double a2 = c.x-a.x,b2=c.y-a.y,c2=(a2*a2+b2*b2)/2;
    double d=a1*b2-a2*b1;
    center.x=a.x+(c1*b2-c2*b1)/d;
    center.y=a.y+(a1*c2-a2*c1)/d;
    return center;
}
//求最小圆覆盖,返回圆心c和半径r:
void min_cover_circle(Point *z, int n,Point &c, double &r)
{
    random_shuffle(z,z+n);             //打乱所有点
    c = z[0];
    r = 0;                    //第一个点
    for (int i = 1; i < n; ++i)        //剩下所有点
    {
        if (sgn(Distance(z[i],c)-r)>0) //pi在圆外部
        {
            c=z[i];
            r=0;                //将圆心设为pi半径为0
            for (int j = 0; j < i; ++j) //重新检查前面的点
            {
                if (sgn(Distance(z[j],c)-r)>0)//两点定圆
                {
                    c.x=(z[i].x+z[j].x)/2;
                    c.y=(z[i].y+z[j].y)/2;
                    r=Distance(z[j],c);
                    for (int k = 0; k < j; ++k)
                    {
                        if (sgn(Distance(z[k],c)-r)>0)
                        {
                            c=circle_center(z[i],z[j],z[k]);
                            r=Distance(z[i],c);
                        }
                    }
                }
            }
        }
    }
}
Point z[maxn];
Point x[maxn];//y,z
Point y[maxn];//x,z
int main()
{
    int n;
    Point x_c, y_c, z_c;
    double z_r, y_r, x_r;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i)
    {
        double a, b, c;
        scanf("%lf%lf%lf", &a, &b, &c);
        z[i].x = a;
        z[i].y = b;
        x[i].x = b;
        x[i].y = c;
        y[i].x = a;
        y[i].y = c;
        //scanf("%lf%lf",&z[i].x,&z[i].y);
    }
    min_cover_circle(z,n,z_c,z_r);
    min_cover_circle(x,n,x_c,x_r);
    min_cover_circle(y,n,y_c,y_r);
    printf("%.10f\n", 2 * min(x_r, min(y_r, z_r)));
    return 0;
}

C.New Maths

题意
a为amam-1…a2a1;
b为bnbn-1…b2b1;
c为ckck-1…c2c1;
定义 c = a ⊕ b运算规则:
ci = (ai + bi)mod 10;
例 168 ⊕ 75 = 133
定义c = a ⊗ b运算规则:
ck = a0 * bk ⊕ a1 * bk−1⊕⋯⊕ak−1 * b1 ⊕ ak * b0
例 9 ⊗ 1234 = 9876
输入n
求最小a,使得a ⊗ a = n;
n不超过25位;
思路
这道题首先要理解它的运算规则,题目给的例子还是错的
如果从数学角度入手找规律之类的话是非常困难的想说不行又怕被打脸
那么就会有一个朴素的想法即从较低位开始枚举a的每一位,所以想到用搜索来解决这道题,但不加任何优化的话会发现复杂度会高达1e25显然是行不通的;
此时可以注意到因为a的位数显然等于(n + 1)/ 2;
简单证明 令k等于(n + 1)/ 2 + 1;
若ak不为0,则c2k必不为0,同理可证更高位;
令k等于(n + 1)/ 2;
若ak等于0,则cn等于0,矛盾。
因此复杂度可以下降到1e13;
又因为在每个位数上只有相等才能向下搜素因此自身包含大量剪枝然后抱着试一发的心态就过了
细节上注意大于(n + 1)/ 2的位置仍需以0验算一下就行了;

#include<bits/stdc++.h>
using namespace std;
int c[50];
int a[50];
int n;
long long ans = 0x3f3f3f3f3f3f3f3f;
bool dfs(int k){
	if(k == n){
		long long num = 0;
		for(int i = (n + 1) / 2 - 1 ; i >= 0 ; i--)num = num * 10 + a[i];
		ans = min(ans, num); 
		return 1;
	}
	int f = 0; 
	for(int i = 0 ; i <= 9 ; i++){
		int num = 0;
		a[k] = i;
		for(int j = 0 ; j <= k ; j++){
			num += a[j] * a[k - j];
		} 
		if(num % 10 == c[k]){
			if(dfs(k + 1)){
				f = 1;
			}
		}
		if(k >= (n + 1) / 2)break;
	}
	return f;
}
int main(){
	string s;
	cin >> s;
	n = s.length();
	for(int i = 0 ; i < n ; i++){
		c[n - i - 1] = s[i] - '0';
	}
	if(dfs(0)){
		cout << ans;
	}
	else cout << "-1";
}

D.Some Sum

题意
取n个连续整数判断其和的奇偶性。
思路
判断n mod 2,n mod 3即可;

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;
    cin >> n;
    if(n % 2)cout << "Either\n";
    else if((n / 2) % 2)cout << "Odd\n";
    else cout << "Even\n";
}

E. Early Orders

题意
输入n个数,从中找一个1到k的最小字典序子序列
题解
做法是用一个单调栈,每次要压入一个数时,如果小于栈顶的数且栈顶的数不是最后一个则将栈顶的数pop掉
证明 若后面还有栈顶的数,则相当于将一个更小的数置于更前得位置,便能得到一个更小的字典序。若后面无则为了保证这个数被取到就无法弹出。

#include<bits/stdc++.h>
using namespace std;
int a[200005], last[200005];
int ans[200005], f[200005];
int main(){
	int n, k;
	cin >> n >> k;
	for(int i = 1 ; i <= n ; i++){
		cin >> a[i];
		last[a[i]] = i;
	} 
	int top = 0;
	memset(f, 0, sizeof(f));
	for(int i = 1 ; i <= n ; i++){
		if(f[a[i]])continue;
		while(top && a[i] < ans[top] && last[ans[top]] > i){
			f[ans[top]] = 0;
			top--;
		}
		ans[++top] = a[i];
		f[a[i]] = 1;
	}
	for(int i = 1 ; i <= k ; i++){
		cout << ans[i] << ' ';
	}
}

F. Pulling Their Weight

题意
输出一个最小t, 使得所有小于t的数,与所有大于t的数之和相等
题解
因为题目保证了答案存在,所有仅需将数组从小到大排序后,累加与并于总合的一半相比较。
输出时分三种情况讨论即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[100005];
int main(){
	int n;
	cin >> n;
	ll sum = 0;
	for(int i = 1 ; i <= n ; i++){
		cin >> a[i];
		sum += a[i];
	}
	sort(a + 1, a + 1 + n);
	ll sum_t = 0;
	for(int i = 1 ; i <= n ; i++){
		sum_t += a[i];
		if(2 * sum_t > sum){
			cout << a[i];
			break;
		}
		else if(2 * sum_t == sum){
			if(a[i] == a[i + 1]){
				cout << a[i];
				break;
			}
			else{
				cout << a[i] + 1;
				break;
			}
		}
	}
}

G. Birthday Paradox

题意
m个人,n组生日(可能这一天就一个人过生日,也可能多个人过生日),一年365天,问给定的这种情况的概率并取log10输出。
题解
概率题
首先可以确定是可能情况数 / 总情况数
可以定义总人数sum,易求的总情况数为365的sum次;
可能情况可以分为三部分讨论
1 365天中取n天可能数为 C 365 n C_{365}^{n} C365n
2 n组在n天内任意排列,但需要舍去人数相同情况,此时定义数组d储存相同人数出现过的次数。
这里的可能性有n!/ (d[i]!的积);(这里n!可与上方组合数除数抵消)
3 sum个人可以任意排列,但同样要舍去分在同一组的情况
这里的可能性有sum!/ (a[i]! 的积);
最后注意一下控制精度输出

#include<bits/stdc++.h>
using namespace std;
int a[500];
int d[500]; 
int main(){
	int n, sum = 0;
	cin >> n;
	for(int i = 1 ; i <= n ; i++)cin >> a[i], sum += a[i], d[a[i]]++;;
	double ans = 0;
	for(int i = 1 ; i <= sum ; i++)ans += log(i) / log (10) - log(365) / log(10);
	for(int i = 0 ; i < n ; i++)ans += log(365 - i) / log(10);
	for(int i = 1 ; i <= n ; i++){
		for(int j = 2 ; j <= a[i]; j++){
			ans -= log(j) / log(10);
		}
	}
	for(int i = 1 ; i <= 100 ; i++){
		if(d[i]){
			for(int j = 2 ; j <= d[i] ; j++){
				ans -= log(j) / log(10);
			} 
		}
	}
	cout << fixed << setprecision(10) << ans;
}

H. On Average They’re Purple

题意
边可标为两种颜色
给出一个无向图,求1到n的路径最短,但相邻边颜色变化数最多。
题解
求出最短路后减一既是答案

#include<bits/stdc++.h>
using namespace std;
struct node{
	int v, w;
	node(){}
    node(int a, int b){v = a, w = b;}
    bool operator < (const node& obj) const
    {
        if(w == obj.w)
            return v < obj.v;
        return w > obj.w;
    }
};
vector<node> m[100005];
int n, mm;
int ans[100005];
void dij()
{
    memset(ans, 0x3f, sizeof(ans));
    priority_queue<node> q;
    ans[1] = 0;
    q.push(node(1, 0));
    while(!q.empty())
    {
        node now = q.top();
        q.pop();
        for(int i = 0; i < m[now.v].size(); i++)
        {
            node to = m[now.v][i];
            if(ans[to.v] > now.w + 1)
            {
                ans[to.v] = now.w + 1;
                q.push(node(to.v, ans[to.v]));
            }
        }
    }
}
int main(){
	cin >> n >> mm;
	for(int i = 1 ; i <= mm ; i++){
		int u, v;
		cin >> u >> v;
		m[u].push_back(node(v, 1));
		m[v].push_back(node(u, 1));
	}
	dij();
	cout << ans[n] - 1;
} 

J.This Ain’t Your Grandpa’s Checkerboard

题意
给定n * n的矩阵;
判断每一行每一列黑白块是否数量相同且不超过三个连续。
思路
n较小,n方模拟即可;

#include<bits/stdc++.h>
using namespace std;
int n;
char c[50][50];
int main(){
	cin >> n;
	int i, j;
	for(i = 1 ; i <= n ; i++){
		for(j = 1 ; j <= n ; j++)cin >> c[i][j];
	}
	for(i = 1 ; i <= n ; i++){
		int num1 = 0, num2 = 0;
		for(j = 1 ; j <= n ; j++){
			if(j >= 3 && c[i][j] == c[i][j - 1]){
				if(c[i][j - 1] == c[i][j - 2])break;
			}
			if(j >= 3 && c[j][i] == c[j - 1][i]){
				if(c[j - 1][i] == c[j - 2][i])break;
			}
			if(c[j][i] == 'W')num2++;
			if(c[i][j] == 'W')num1++;
		}
		if(j <= n || num1 != n / 2 || num2 != n / 2)break;
	}
	if(i <= n)cout << "0\n";
	else cout << "1\n"; 
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值