round 2 题解
这次更多考验算法和编码能力。
题目难度
由易到难
- A < F < B
- D E C
A 三角形的类型 几何题 难度 1.5
有多种方法可以做这道题。
需要注意一些点。
- 三点成一条直线的情况
- 两个点重合时,三个点重合的情况。
- 注意精度问题。
- 如果采用求直线斜率,要注意不能除0。
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn = 1e5+5;
const int inf = 0x3f3f3f3f;
typedef long long ll;
double eps = 0.0001;
ll x1,y11,x2,y2,x3,y3;
double cal(double x1,double x2,double y11,double y2){
return sqrt( (x2-x1)*(x2-x1)+(y2-y11)*(y2-y11) );
}
void solve(){
scanf("%lld%lld%lld%lld%lld%lld",&x1,&y11,&x2,&y2,&x3,&y3);
double l1 = cal(x1,x2,y11,y2);
double l2 = cal(x1,x3,y11,y3);
double l3 = cal(x3,x2,y3,y2);
if(abs(l1)<eps||abs(l2)<eps||abs(l3)<eps){
cout<<"No"<<endl;
return ;
}
if(l1 == l2 && l2 == l3){
cout<<"equilateral triangle"<<endl;
return ;
}
if(l2 > l1 && l2 > l3) swap(l1,l2);
if(l3 > l1 && l3 > l2) swap(l1,l3);
//此时l1最大
if(l2+l3 == l1){
cout<<"No"<<endl;
return ;
}
double ll = l2*l2 + l3*l3;
l1 = l1*l1;
if(abs(ll - l1) < eps){
if(abs(l2-l3)<eps){
cout<<"Isosceles Right Triangle"<<endl;
}else
cout<<"right triangle"<<endl;
}else if(ll - l1 < eps){
cout<<"obtuse triangle"<<endl;
}else{
cout<<"acute triangle"<<endl;
}
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}
B 字符串的秘密 字符串 + 前缀和模板 难度 2.0
用前缀和,把字母替换成对应的权值。
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1e5;
typedef long long ll;
ll sum[N];
int main()
{
int n,q;
cin>>n>>q;
getchar();
for(int i = 1;i<=n;i++){
char ch = getchar();
if(ch == ' '){
sum[i] = sum[i-1];
}else{
sum[i] = sum[i-1] + ch-'a'+1;
}
}
for(int i = 1;i<=q;i++){
ll l,r;
scanf("%lld%lld",&l,&r);
printf("%lld\n",sum[r]-sum[l-1]);
}
return 0;
}
C 谁是博弈滴神 思维 博弈 难度2.5
每个人都要采取最优的策略,来保证自己最大概率获胜。
那么先行动的人就有优先选择权。
如果从必胜的角度不好考虑,可以从对方必败的角度考虑。
如果小w把石子分成两个一模一样的部分,这两部分不能相连。
那么无论小y怎么取A部分,小w只要模仿小y同样取B部分就可以保证必胜。
反之 如果小w无法把石子分成两个一模一样的部分,那么小y只需要用同样的策略就可以获胜。
小w的必胜态:
k >= 2 : 无论n怎么样,都能分成两部分一模一样。
n是奇数 : 只要n是奇数个,无论k怎么样,都能分成一模一样的两部分。
小y的必胜态:
k == 1 && n是偶数: 小w无论怎么取,都会留下一奇一偶两部分 ,(偶部分可能为0个,奇部分最少为1个)
对于奇部分,小y可以将其分成两部分一模一样的,这样奇数部分小y一定最后取完。
对于偶部分,留给小w先取,然后又回到了上面的情况。
#include <stdio.h>
int main()
{
int n, k;
scanf("%d %d",&n,&k);
if(n == 0){
printf("y yyds!\n");
}
else if(k ==
f("w yyds!\n");
}
}
else{
printf("w yyds!\n");
}
return 0;
}
D 搭积木 最长上升子序列 难度 2.0
求最长上升子序列。
答案是n-最长上升子序列的长度。
n方的dp做法和nlogn的二分做法都能过。
因为a可以改变为任意大于0的实数。
那么只要得出最长上升子序列后,其他数按照该上升序列构造一个满足严格单增的序列即可。
二分做法。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
// O(nlogn)
const int N = 1e6+5;
ll b[N];
int main()
{
int n;
cin>>n;
ll cnt = 1;
for(int i = 1;i<=n;i++){
ll x;
scanf("%lld",&x);
ll id = lower_bound(b+1,b+cnt,x)-b;
// cout<<id<<endl;
b[id] = x;
if(id == cnt) cnt++;
}
// for(int i = 1;i < cnt;i++){
// cout<<b[i]<<" ";
// }
cout<<n-cnt+1;
return 0;
}
n方的DP做法
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N = 1e6+5;
ll a[N];
ll f[N];
//O(n*n)
int main()
{
int n;
cin>>n;
for(int i = 1;i<=n;i++){
scanf("%lld",&a[i]);
}
ll maxlen = -1;
for(int i = 1;i<=n;i++){
ll now = 1;
for(int j = i-1;j >= 1;j--){
if(a[j] < a[i]){
now = max(now,f[j]+1);
}
}
f[i] = now;
maxlen = max(maxlen,f[i]);
}
// for(int i = 1;i<=n;i++){
// cout<<f[i]<<" ";
// }
cout<<n-maxlen;
return 0;
}
E 英雄联盟 多种做法 模拟 思维 难度2.0
有很多种做法:
- 模拟
- 差分
- https://www.luogu.com.cn/problem/solution/P1969
1. 模拟
模拟的做法:递归。
我们可以把加血变成从满血状态扣血。
每次技能的范围都尽可能地大。
那么就变成了求解每一个不包含0的区间[l,r]
求解lr区间,我们先找到区间的最小值。然后把区间里的所有元素都减去该最小值,得到被零划分成多个的子区间。
该区间的答案就是区间最小值 + 所有子区间的答案。
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N = 1e5+5;
const ll inf = 1e18;
ll a[N];
int n;
ll cal(int l,int r){
if(l > r) return 0;
if(l == r) {
ll ans = a[l];
a[l] = 0;
return ans;
}
ll minn = inf;
for(int i = l;i<=r;i++){
minn = min(minn,a[i]);
}
int pre = l;
ll ans = minn;
for(int i = l;i <= r;i++){
a[i] -= minn;
if(a[i] == 0){
ans += cal(pre,i-1);
pre = i+1;
}
}
if(a[r] != 0)
ans+=cal(pre,r);
return ans;
}
int main()
{
cin>>n;
for(int i = 1;i <= n;i++){
scanf("%lld",&a[i]);
}
cout<<cal(1,n);
return 0;
}
2. 差分
把序列分成(a1,…ai) (ai+1,…aj) … (ak,…an) 多个非递减序列。
然后把每段序列的最大值加起来 的 sum。
无论如何答案都不可能超过sum。
那么还能继续减少,即减去除了第一个序列外的最小值min。
因为我们可以提前先用所有大于min的元素减去这个最小值min。
#include<stdio.h>
int n;
int x;
int ans = 0;
int main()
{
scanf("%d",&n);
scanf("%d",&x);
ans += x;
int cur = x;
for(int i = 1;i < n;i++){
scanf("%d",&x);
if(x>=cur){
ans += (x - cur);
}
cur = x;
}
printf("%d",ans);
return 0;
}
F 试炼塔1.0 逆元 + 快速幂 难度1.5
主要考察逆元和快速幂(模板代码在题面)。
公式是
(1/q) 的n次方 乘 (n*(n+1))/2
对于求逆元,我们需要把分母全部求其对1e9的逆元。
然后分子乘上所有的逆元。
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
/*******快速幂********/
ll powerMod(ll x, ll n, ll m){ //求解 x的n次方 并求模于m
if(n==0) return 1;
ll res = 1;
while (n > 0){
if (n & 1) // 判断是否为奇数,若是则true
res = (res * x) % m;
x = (x * x) % m;
n >>= 1; // 相当于n /= 2;
}
return res;
}
//求k ,k = b关于p的逆元
ll inv(ll b, ll mod)
{
return powerMod(b,mod-2,mod); //powerMod为快速幂,函数定义在上面
}
int main()
{
ll n,q;
while(scanf("%lld%lld",&n,&q)!=EOF){
ll inv2 = inv(2,mod)%mod;
ll invq = inv(q,mod)%mod;
ll ans = ( n*(n+1) ) % mod;
ans = (ans*inv2)%mod;
ans = (ans * powerMod(invq,n,mod))%mod;
printf("%lld\n",ans);
}
return 0;
}
比赛中出现的点:
- 读入一行字符串(包括空格)时,需要用getline或者getchar()一个个字符读入。
- 除法一定记得要用逆元。