2020牛客寒假算法基础集训营1

honoka和格点三角形

题目描述

honoka最近在研究三角形计数问题。
她认为,满足以下三个条件的三角形是“好三角形”。
1.三角形的三个顶点均为格点,即横坐标和纵坐标均为整数。
2.三角形的面积为 。
3.三角形至少有一条边和 轴或 轴平行。
honoka想知道,在平面中选取一个大小为 的矩形格点阵,可以找到多少个不同的“好三角形”?由于答案可能过大,请对 取模。

输入描述

两个正整数n和m( 2\ ≤n,m≤10^9)(2 ≤n,m≤109)

输出描述

面积为1的格点三角形的数量,对 10^9+7109+7 取模的结果。

示例

示例1

输入
2 3
输出
6

说明

不妨设左下角坐标为(1,1),右上角坐标为到(3,2)。
那么三点坐标可选:
(1,1)(1,2)(3,1)
(1,1)(1,2)(3,2)
(1,1)(2,2)(3,1)
(1,1)(3,1)(3,2)
(1,2)(2,1)(3,2)
(1,2)(3,1)(3,2)
所以共有6个。

示例2

输入
100 100

输出
7683984

解题思路

要想面积为一,且每个顶点都在格点上,所以我么们可以选的高为1,底为2(一)或是高为2,底为1(二)的两种三角形。
在这里插入图片描述
我们有n行和m列。

当(一)这种情况时:
我们先选每行中为1的底,可选择的个数,显而易见我们每行可以选择m-1个底。因为高为二所以我们要跨一行去选每一个点,而这一行上的每一个点都可以选择,所以我们可以得出这样的结论每三行都可以选择(m-1)m个三角形,所以在所有行中选择我们就可以选择(m-1)m(n-2)个三角形。同理我们从下往上选是一样的所以只需乘2即可。
我们还可以从每一列选底,这种情况即和上述情况所总结出来的式子的n和m互换位置即可。
当(二)这种情况时:
同样的我们选每行中为2的底,我们每行可以选择m-2个底。因为高为1,所以我们只需在下一行选择第三个点即可,如图中二所示,两个用蓝线画出来的三角形会和第一种情况重合所以我们要删去这两个点,因此我们每行只能选m-2个符合条件的第三个点。所以我们从上述可以得出结论每两行我们可以选出(m-2)(m-2)个三角形,然后我们再拓展到每一行,即可选出(m-2)(m-2)(n-1),同一我们还是可以从下往上选,乘2即可。
这种情况我们同样可以从每一列中选底,也是将结论中的n和m互换位置即可。
注意:
因为题目中所给的数据较大,所以我们每做一次乘的运算的时候都要取一次模,防止数据过大出现错误,如果将式子一次算出再取模,提交后就会显示无法通过全部案例。

代码

#include<iostream>
using namespace std;
int main(){
 long long n,m;
 cin>>n>>m;
 long long z,a,b,c,d;
 a=(n-1)*n%1000000007;
 a=a*(m-2)*2%1000000007;
 b=(m-1)*m%1000000007;
 b=b*(n-2)*2%1000000007;
 c=(n-2)*(n-2)%1000000007;
 c=c*(m-1)*2%1000000007;
 d=(m-2)*(m-2)%1000000007;
 d=d*(n-1)*2%1000000007; 
 z=a+b+c+d;
 cout<<z%1000000007;
}

kotori和bangdream

题目描述

有一天,kotori发现了一个和lovelive相似的游戏:bangdream。令她惊讶的是,这个游戏和lovelive居然是同一个公司出的!
kotori经过一段时间的练习后已经变得非常触,每个音符 x%x%x% 的概率perfect,获得 分, (100−x)%(100 -x)%(100−x)% 概率great,获得 分。
已知一首歌有 个音符。kotori想知道,不考虑连击加成的话,一首歌得分的期望是多少?

输入描述

一行 个整数,用空格隔开。分别是 n,x,a,b。(0≤n≤100),(1≤x,a,b≤1000)

输出描述

** 一首歌得分的期望,保留两位小数。**

示例

示例1

输入
100 50 500 400

输出
45000.00

说明
如果全perfect是50000分,全great是40000分。由于它们的概率都是50%,即perfect和great五五开,所以期望是45000分。

解题思路

我们只需按题目要求列出式子即可。(别忘了保留两位小数哟)

代码

#include<iostream>
#include<stdio.h>
using namespace std;
int main(){
 double n,x,a,b;
 cin>>n>>x>>a>>b;
 double z=n*x*0.01*a+n*(100-x)*0.01*b;
 printf("%.2f",z);
} 

umi和弓道

题目描述

umi对弓道非常痴迷。
有一天,她在研究一个射箭问题:
在一个无限大的平面中,她站在 这个坐标。
有 个靶子,第 个靶子的坐标是
umi准备在 轴或 轴上放置一块挡板来挡住弓箭的轨迹,使得她可以射中的靶子数量不超过 个。
她想知道挡板的最短长度是多少?
注:假定弓箭的轨迹是起点为umi坐标、长度无穷大的射线。umi和靶子的体积可以无视。挡板的边缘碰到弓箭轨迹也可视为挡住弓箭。
注2:挡板不能弯折,起始和终点必须在同一坐标轴上。

输入描述

第一行两个整数 x0,y0 ,代表umi的坐标。
第二行两个正整数 n和k ,分别代表靶子的总数量、放置挡板后可射中靶子的最大值。
接下来的n行,每行两个整数xi和yi 。代表每个靶子的坐标。
保证没有任何一个点在坐标轴上(无论umi还是靶子),保证没有任何两点重合。
(1≤n≤100000,0≤k≤n-2,-2109≤2109)

输出描述

若无论如何无法保证可以射中的靶子数量不超过k个,则输出-1。
否则输出挡板的最小长度。如果你和正确答案的误差不超过10-6,则视为答案正确。

示例

示例1

输入
1 1
2 0
-1 2
-2 1

输出
0.50000000

说明
umi要保证能射中的靶子不超过0个,即全部挡住。在y轴上选区间[1,1.5]放置一个长度为0.5的挡板即可。

解题思路

在同一象限的一定不可能用挡板挡上,所以我们只需研究不同象限的即可,题意就是如图所示求最小的挡板长度(即红线所画)在这里插入图片描述
在这里插入图片描述

如上图所示靶子可以分为四种情况,A一定不能被挡,B要在y轴上安装挡板,D要在x轴上安装挡板才能挡住,而C既可以在y轴上装挡板,又可以在x轴上装挡板。
所以我们只需考虑xx0<0即靶子和主人公在y轴的两侧,需要在y轴上安装挡板,所以我们设一个ly[]的数组来存两点间与y轴的交点即y0+x0(y0-y)/(x-x0)
或是yy0<0即靶子和主人公在x轴的两侧,需要在x轴上安装挡板,所以我们设一个lx[]的数组来存两点间与x轴的交点x0+y0(x0-x)/(y-y0)
然后我们用先对两个数组排序,再用需要挡住个数的固定长度来截取两个数组,取最小值即可得到答案。

代码

#include<bits/stdc++.h>
using namespace std;
double lx[100005],ly[100005];
bool cmp(double a,double b){
 return a>b;
}
int main(){
 double x0,y0;
 int n,k;
 cin>>x0>>y0;
 cin>>n>>k;
 double a,b;
 int j=0,z=0;
 for(int i=0;i<n;i++){
  cin>>a>>b;
  if(a*x0<0){
   ly[j]=y0+x0*(y0-b)/(a-x0);
   j++;
  }
  if(b*y0<0){
   lx[z]=x0+y0*(x0-a)/(b-y0);
   z++;
  }
 }
 sort(ly,ly+j,cmp);
 sort(lx,lx+z,cmp);
 int i=0,o=n-k-1;
 double li=1e10;
 while(o<j&&o>0){
  li=min(li,ly[i]-ly[o]);
  i++,o++;
 }
 i=0,o=n-k-1;
 while(o<z&&o>0){
  li=min(li,lx[i]-lx[o]);
  i++,o++;
 }
 if(li!=1e10) printf("%.7lf",li);
 else{
  cout<<-1;
 } 
} 

hanayo和米饭

题目描述

hanayo很喜欢吃米饭。
有一天,她拿出了n个碗,第一个碗装了1粒米饭,第二个碗装了2粒米饭,以此类推,第n个碗装了n粒米饭。
然而,爱搞恶作剧的rin把所有的碗的顺序打乱,并拿走了一个碗。hanayo想知道,rin拿走的碗里有多少粒米饭?

输入描述

第一行输入一个正整数n。代表原始的总碗数。
第二行输入n-1个正整数ai ,代表目前每碗里米饭数量。
保证输入合法。

(2≤n≤100000,1≤ai≤n)

输出描述

输出一个正整数,代表rin拿走的碗里米饭数量。

示例

示例1

输入
5
2 5 1 3

输出
4

说明
开始共有5个碗,每个碗内分别有1、2、3、4、5粒米饭。rin拿走的是第四碗。这么简单的样例连tairitsu都看得懂好伐~

解题思路

先建立一个数组a[],每输入完一个数使a[x]=1,输入完之后从1~n遍历一遍,一直到发现a[i]不等于1时退出了,输出当前的i值即使拿走的米饭数量。

代码

#include<iostream>
using namespace std;
int a[100005];
int mai*n(){
 int n;
 int b;
 cin>>n;
 for(int i=1;i<=n-1;i++){
  cin>>b;
  a[b]++;
 }
 int i;
 for(i=1;i<=n;i++){
  if(a[i]!=1) break;
 }
 cout<<i;
}

rin和快速迭代

题目描述

rin最近喜欢上了数论。
然而数论实在太复杂了,她只能研究一些简单的问题。
这天,她在研究正整数因子个数的时候,想到了一个“快速迭代”算法。设f(x)为x的因子个数,将f迭代下去,rin猜想任意正整数最终都会变成2。
例如:
f(12)=6 , f(6)=4 , f(4)=3 , f(3)=2 。
她希望你帮她验证一下。她会给你一个正整数 ,让你输出它在迭代过程中,第一次迭代成 的迭代次数。

输入描述

一个正整数n(3≤n≤1012)。

输出描述

一个正整数,为从n到2的迭代次数。

示例

示例1

输入
12

输出
4

说明
12的因子:1,2,3,4,6,12。共6个。6的因子:1,2,3,6。共4个。4的因子:1,2,4。共3个。3的因子:1,3。共2个。12 → 6 → 4 → 3 → 2 , 故迭代了4次。

解题思路

这道题做都会做,最主要的时不超时,超时的点就在,一个数因子个数的判断。所以我们为了省时间,从1~sqrt(n)来一遍遍历,因为因子都是一对一对出现的除了i*i=n的这种情况,来统计因子个数

代码

#include<iostream>
using namespace std;
int ans=0;
long long yzs(long long n){
 long long a=n;
 long long num=2;
 for(long long i=2;i*i<=n;i++){
  if(a%i==0){
   if(i*i!=n) num+=2;
   else num+=1;
  }
 }
 return num;
}
int main(){
 long long m;
 cin>>m;
 long long num=m;
 while(num!=2){
  num=yzs(num);
  ans++;
 }
 cout<<ans;
}

maki和tree(未做出)

题目描述

有一天,maki拿到了一颗树。所谓树,即没有自环、重边和回路的无向连通图。
这个树有 n个顶点,n-1 条边。每个顶点被染成了白色或者黑色。
maki想知道,取两个不同的点,它们的简单路径上有且仅有一个黑色点的取法有多少?
注:
①树上两点简单路径指连接两点的最短路。
② <q,p>和<p,q>的取法视为同一种。

输入描述

第一行一个正整数n。代表顶点数量。(1≤n≤100000)
第二行是一个仅由字符’B’和’W’组成的字符串。第i个字符是B代表第i个点是黑色,W代表第i个点是白色。
接下来的n-1行,每行两个正整数x,y,代表x点和y点有一条边相连
(1≤x,y≤n)

输出描述

一个正整数,表示只经过一个黑色点的路径数量。

示例

示例1

输入
3
WBW
1 2
2 3

输出
3

说明
树表示如下:在这里插入图片描述
其中只有2号是黑色点。
<1,2>、<2,3>、<1,3>三种取法都只经过一个黑色点。

eli和字符串

题目描述

li拿到了一个仅由小写字母组成的字符串。
她想截取一段连续子串,这个子串包含至少k个相同的某个字母。
她想知道,子串的长度最小值是多少?
注:所谓连续子串,指字符串删除头部和尾部的部分字符(也可以不删除)剩下的字符串。例如:对于字符串“arcaea”而言,“arc”、“rcae”都是其子串。而“car”、“aa”则不是它的子串。

输入描述

第一行输入两个正整数n和k(1≤k≤n≤200000),
输入仅有一行,为一个长度为n的、仅由小写字母组成的字符串。

输出描述

如果无论怎么取都无法满足条件,输出-1 。
否则输出一个正整数,为满足条件的子串长度最小值。

示例

示例1

输入
5 2
abeba

输出
3

说明
选择“beb”子串,长度为3,其中包含相同的两个’b’

解题思路

这道题赛后能做出来,完全是因为前一天的umi和弓道这道题看完题解并完全弄懂,所以说赛后补题真的很重要,所以这道题我用的和c题是一样的方法。

我们先设一个结构体数组用来存放字母,里面包括字母的位置和个数,然后我们只需对这26个字母进行遍历,先判断字母的个数是否大于等于k,然后再用固定长度(k)来对每个字母的进行最小长度的寻找,最后我们即可求出答案。

代码

#include<bits/stdc++.h>
using namespace std;
struct zm{
 int wz[200000];
 int num;
}a[26];
int main(){
 int n,k;
 cin>>n>>k;
 char ch[200005];
 cin>>ch;
 int flag=0;
 for(int i=0;i<n;i++){
  a[ch[i]-'a'].wz[a[ch[i]-'a'].num]=i;
  a[ch[i]-'a'].num++;
  if(a[ch[i]-'a'].num>=k) flag=1;
 }
 if(flag==0){
  cout<<-1;
  return 0;
 } 
 else{
  int mi=1e18;
     int l;
  for(int i=0;i<26;i++){
   int head=0,tail=k-1;
   if(a[i].num>=k){
    while(tail<a[i].num){
     l=a[i].wz[tail]-a[i].wz[head]+1;
     mi=min(l,mi);
     head++;
     tail++;
    }
   }
  }
  cout<<mi;
 }
}

nozomi和字符串

题目描述

nozomi看到eli在字符串的“花园”里迷路了,决定也去研究字符串问题。
她想到了这样一个问题:
对于一个 “01”串而言,每次操作可以把 0字符改为 1 字符,或者把 1字符改为 0字符。所谓“01”串,即只含字符 0和字符 1的字符串。
nozomi有最多k次操作的机会。她想在操作之后找出一个尽可能长的连续子串,这个子串上的所有字符都相同。
nozomi想问问聪明的你,这个子串的长度最大值是多少?
注:看、次操作机会可以不全部用完。
如果想知道连续子串的说明,可以去问问eli,nozomi不想再讲一遍。

输入描述

第一行输入两个正整数n和 k(1≤n≤k≤200000)
输入仅有一行,为一个长度为n的、仅由字符 0 和 1组成的字符串。

输出描述

一个正整数,为满足条件的子串长度最大值。

示例

示例1

输入
5 1
10101

输出
3

说明
只有1次操作机会。
将第二个位置的 0改成 1 ,字符串变成 11101,可以选出 “111”子串,长度为3。
如果修改第三个或者第四个位置的字符也可以选出长度为3的子串。

解题思路

这题与c题和上一题同一思路,我们还是用两个数组存0或1的位置,因为我么要改,不是全改成1就是全该成0,所以我们反过来看,若是要全改成1我们就看0的位置,我们先选取要改的k个0,用head,和tail来遍历,而a[tail+1]-a[head-1]-1就是我们所要的改过k个0的长度。然后再用同样的方法找全改成0的最长长度,找出两者的最大值即可。

代码

#include<bits/stdc++.h>
using namespace std;
int main(){
 int n,k;
 cin>>n>>k;
 int l0[200000],l1[200000];
 char ch[200005];
 cin>>ch;
 int z=1,j=1;
 l0[0]=-1,l1[0]=-1;
 for(int i=0;i<n;i++){
  if(ch[i]=='0'){
   l0[z]=i;
   z++;
  }
  else{
   l1[j]=i;
   j++;
  }
 }
 l0[z]=n,l1[j]=n;
 int ma=0;
 int l;
 if(z-1>=k){
  int head=1,tail=k;
  while(tail<z){
   l=l0[tail+1]-l0[head-1]-1;
   ma=max(ma,l);
   head++,tail++;
  }
 }
 else ma=n;
 if(j-1>k){
  int head=1,tail=k;
  while(tail<=j){
   l=l1[tail+1]-l1[head-1]-1;
   ma=max(ma,l);
   head++,tail++;
  }
 }
 else ma=n;
 cout<<ma;
}

nico和niconiconi

题目描述

nico平时最喜欢说的口头禅是niconiconi~。
有一天nico在逛著名弹幕网站"niconico"的时候惊异的发现,n站上居然有很多她的鬼畜视频。其中有一个名为《让nico为你洗脑》的视频吸引了她的注意。
她点进去一看,就被洗脑了:“niconicoh0niconico^vvniconicoG(vniconiconiconiconiconicoG(vniconico…”
弹幕中刚开始有很多“nico1 nico2”等计数菌,但到后面基本上都是“计数菌阵亡”的弹幕了。
nico也想当一回计数菌。她认为:“nico” 计a分,“niconi” 计b分,“niconiconi” 计c分。
她拿到了一个长度为n的字符串,请帮她算出最大计数分数。
注:已被计数过的字符不能重复计数!如"niconico"要么当作"nico"+“nico"计2a分,要么当作"niconi”+"co"计b分。

输入描述

第一行四个正整数n,a,b,c。 (1≤n≤300000,1≤a,b,c≤109)
第二行是一个长度为n的字符串。

输出描述

一个整数,代表最大的计数分数。

示例

示例1

输入
19 1 2 5
niconiconiconiconi~

输出
7

说明
“niconi"co"niconiconi”~
故为2+5=7分

解题思路

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值