湖南大学第十六届程序设计竞赛(补题)
2021.7.15 13-18 p.m.
A Triangles
题意 :给出三个点坐标判断三角形形状(可能不能构成三角形)、
思路 :
首先判断三点确定的直线斜率是否相等,防止除法精度丢失,化成乘法 (x0-x1)(y1-y2)==(x1-x2)(y0-y1)
判断形状直接算出 三边的长度的平方 ,用最大边的余弦定律判断形状
代码如下:
#include<bits/stdc++.h>
#define ri int
typedef int lll;
typedef long long ll;
using namespace std;
const ll mod=1e9+7;
inline ll gcd(ll a,ll b) {
return (!b)?a:gcd(b,a%b);
}
const ll inf=999999999;
int read() {
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*f;
}
void write(int x) {
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
ll t;
ll x[3],y[3];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin >> t;
while(t--)
{
for(ri i=0;i<3;i++) cin >> x[i] >> y[i];
if((x[0]-x[1])*(y[1]-y[2])==(x[1]-x[2])*(y[0]-y[1])) //斜率相等公式交叉相乘
cout << "invalid\n";
else
{
ll len[3];
len[0]=(x[0]-x[1])*(x[0]-x[1])+(y[0]-y[1])*(y[0]-y[1]);
len[1]=(x[0]-x[2])*(x[0]-x[2])+(y[0]-y[2])*(y[0]-y[2]);
len[2]=(x[1]-x[2])*(x[1]-x[2])+(y[1]-y[2])*(y[1]-y[2]);
sort(len,len+3);
ll ans=len[0]+len[1]-len[2]; //余弦定律
if(!ans) cout << "right\n";
else if(ans>0) cout << "acute\n";
else cout << "obtuse\n";
}
}
return 0;
}
B Yuki with emofunc and playf
题意 :初始有一个数1,你每次可以将其×10或者+(x−1)现在给你x,问最少经过多少步能到达n或n的倍数。
思路 :看成(k*10)%n=0 || (k+x-1)%n=0 || k%n=0 三种情况,
也就是化成余数0 -- n-1 ,从k%n开始到0的最短路,每一种方式长度均为1,求出最早到0时的dis[0],即为最短距离
代码如下:
#include <bits/stdc++.h>
#define ri int
typedef int lll;
typedef long long ll;
using namespace std;
const ll mod=1000000007;
const ll inf=999999999;
const ll N=5e5+5;
ll n,x;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin >> n >> x; --x;
ll k=1;
vector<ll> vis(n+5);
vector<ll> dis(n+5);
k=k%n; //从k%n开始
queue<ll> q;q.push(k);
dis[k]=0,vis[k]=1;
ll now=0,tp=0;
while(!q.empty())
{
if(vis[0]) break; //最早到0的结果输出即可
tp=q.front();q.pop();
now=(tp+x)%n;
if(!vis[now]) q.push(now),vis[now]=1,dis[now]=dis[tp]+1;
now=(tp*10)%n;
if(!vis[now]) q.push(now),vis[now]=1,dis[now]=dis[tp]+1;
}
if(!vis[0]) cout << "-1\n"; //到不了0即不存在
else cout << dis[0] << '\n';
return 0;
}
D Queuing
题意 : 给出所在队伍当前的位置(第n位),去m个窗口重新排队,且如果一开始A在B前面,并且A和B冲到同一个窗口,那么A还在B前面
问去窗口排队后所在位置的期望值
思路 :一个窗口时增加位置数,发现每次都加一,两个窗口时增加位置数,发现每次都加1/2。
任何窗口数处于第一位,总是一,故推得公式1+(m-1)*(1.0/n)
代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <cmath>
#include <set>
#include <stack>
#include <queue>
#define ri int
typedef int lll;
typedef long long ll;
using namespace std;
const ll mod=80112002;
const ll inf=999999999;
const ll N=5e4+5;
ll t;
ll n,m;
float ans;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
scanf("%lld%lld",&n,&m);
float add=1.0/n;
ans=1+(m-1)*add;
printf("%.8f",ans);
return 0;
}
F Team
题意 : 三个大数相加
思路 :大数相加板子,签到题
代码如下:
#include<bits/stdc++.h>
#define ri int
using namespace std;
typedef int lll;
typedef long long ll;
const ll mod=1e9+7;
inline ll gcd(ll a,ll b) {
return (!b)?a:gcd(b,a%b);
}
const ll inf=999999999;
vector<ll> add(vector<ll> &A , vector<ll> &B)
{
ll lena=A.size();
ll lenb=B.size();
vector<ll> C;
ll t=0;
for(int i=0;i<lena||i<lenb;i++)
{
if(i<lena) t+=A[i];
if(i<lenb) t+=B[i];
C.push_back(t%10);
t/=10;
}
if(t) C.push_back(1);
return C;
}
ll t;
string a,b,c;
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
vector<ll> A,B,C,D,E;
cin >> a >> b >> c;
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
C=add(A,B);
for(ri i=c.size()-1;i>=0;i--) D.push_back(c[i]-'0');
E=add(C,D);
for(ri i=E.size()-1;i>=0;i--) cout << E[i];
cout << '\n';
return 0;
}
I Binbin and Balls
题意 :有两个球,n层楼,丢球测试出球在多少层为碎的临界点,求最坏情况的最小次数
思路 :由于只有两个球,令可以尝试K次
第一次从第K层丢出 ,如果碎了,第二个球要从1 -> K-1逐个尝试,一共尝试K次。如果没碎,再从第K+K-1层丢出,碎了第二个球从k+1 -> 2k逐个尝试,一共也是K次
因此:尝试K次最多可以试出 k+k-1+k-2+ ... +1层楼
故在给定范围中,用二分查找找出第一个大于等于n楼层的数即为答案。
代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <cmath>
#include <set>
#include <stack>
#include <queue>
#define ri int
typedef int lll;
typedef long long ll;
using namespace std;
const ll mod=80112002;
const ll inf=999999999;
const ll N=5e4+5;
ll t;
ll n;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin >> t;
while(t--)
{
cin >> n;
ll l=0,r=2e9;
while(l<r)
{
ll mid=l+r>>1;
if(mid*(mid+1)/2>=n) r=mid; //k+k-1+k-2+k-3 + ... + 1 >= n
else l=mid+1;
}
cout << l << '\n';
}
return 0;
}
L Cracked Pipes
题意 :六种类型水管,判断给定的种类排列能否把水从起点运送到终点
思路 :
一道纯模拟题,比赛时看都没看,不过模拟过程有小技巧
建图,分管道种类,用方向数组进行dfs,能否进入下一段还要检查是否两者能连接上,最后能到终点即输出"YES" ,不能则输出"NO"
代码如下:
#include <bits/stdc++.h>
#define ri int
typedef int lll;
typedef long long ll;
using namespace std;
const ll mod=80112002;
const ll inf=999999999;
const ll N=5e4+5;
ll n,m;
ll nx[7][4]={{},{-1,0,0,1},{0,-1,1,0},{0,0,1,1},{-1,-1,0,0},{-1,-1,1,0},{0,-1,1,1}};
ll dx[4]={-1,0,0,1};
ll dy[4]={0,-1,1,0};
vector<vector<ll> > mp;
vector<vector<ll> > vis;
void dfs(ll x,ll y)
{
if(vis[n][m]) return;
vis[x][y]=1;
ll xx=0,yy=0;
for(ri i=0;i<4;i++)
{
xx=x+dx[i],yy=y+dy[i];
if(xx>=1&&xx<=n && yy>=1&&yy<=m)
{
if(!vis[xx][yy])
{
if(nx[ mp[x][y] ][i] && nx[ mp[xx][yy] ][3-i]) dfs(xx,yy);
}
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin >> n >> m;
mp.resize(n+5);vis.resize(n+5);
for(ri i=1;i<=n;i++)
{
mp[i].resize(m+5);vis[i].resize(m+5);
for(ri j=1;j<=m;j++)
{
cin >> mp[i][j];
}
}
mp[1][0]=mp[n][m+1]=2;
dfs(1,0);
if(vis[n][m]&&(mp[n][m]==2||mp[n][m]==5||mp[n][m]==6)) cout << "YES\n";
else cout << "NO\n";
return 0;
}