费话不多说,直接开始补题吧(主要也是没啥心情说了)
(题解没有代码,寄)
A 比比谁更大
废话可跳过:在本次比赛A题的位置,一开始做直接写了求阶乘的递归函数,WA了一发。然后看到了数据范围:1e9(这么大的数据范围还写递归真是疯了)。然后注意到题目中给的“很妙的模数”,发现它并不是质数,然后把它质因数分解可写为3*7*11*13*17*19*103。看到这记得之前做过这样类似的题,好像是和103有关但是想不起来了,,,还是对取模运算掌握不熟练。
思路:999068070=3*7*11*13*17*19*103,最大的质因数为103,所以大于等于103的阶乘都是可以整除999068070,即取模后都为0。那么把数分为大于等于103和小于103两部分,大于等于103的部分直接用前面的结论,即都输出两数相等的情况;对于小于103的部分,直接暴力求解即可,我是写的递归函数求阶乘。(记得有几组特判!)
AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <cstring>
#include <cmath>
#include <set>
#include <iterator>
#include <numeric>
using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=999068070;
ll a,b;
int m[1000005];
ll func(ll a)
{
if(a==0) return 1;
else
return func(a-1)*a%mod;
}
int main()
{
// freopen("test.in","r",stdin);
cin>>a>>b;
m[0]=1;
for(int i=1;i<=103;i++)
{
m[i]=i*m[i-1]%mod;
}
if(a>=103&&b>=103)
cout<<"There is no winner!"<<'\n';
else if(a>=103&&b<103)
cout<<"b is the winner!"<<'\n';
else if(b>=103&&a<103)
cout<<"a is the winner!"<<'\n';
else
{
if(func(a)>func(b))
cout<<"a is the winner!"<<'\n';
else if(func(a)<func(b))
cout<<"b is the winner!"<<'\n';
else if(func(a)==func(b))
cout<<"There is no winner!"<<'\n';
}
return 0;
}
B - 过生日
思路: 二维数组处理到第i个字符26个字母个数前缀和,二分查找满足条件的长度。
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=1e9+7;
const int N=2e5+5;
int t,k,cnt[N][28],len;
string s;
bool check(int x)
{
for(int i=x;i<=len;i++)
{
for(int j=0;j<26;j++)
{
if(cnt[i][j]-cnt[i-x][j]>=k)
return true;
}
}
return false;
}
int main()
{
// freopen("test.in","r",stdin);
// freopen("output.in", "w", stdout);
cin>>t;
while(t--)
{
memset(cnt,0,sizeof(cnt));
cin>>s>>k;
len=s.length();
s=" "+s;
for(int i=1;i<=len;i++)
{
for(int j=0;j<26;j++)
cnt[i][j]=cnt[i-1][j];
cnt[i][s[i]-'a']++;
}
int l=0,r=len;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
if(l==len&&!check(l)) cout<<"-1"<<'\n';
else cout<<l<<'\n';
}
return 0;
}
os:二分用法很灵活
C hhgg爱Java
废话可跳过:一开始看题的时候没看懂直接跳了,然后后来看榜上这个题的AC数很多, 然后仔细看了看读懂了(我真fw)。这个题我是用结构体+数组做的,好像有同学是用map做的也可以。
思路:分清楚到底是根据哪个数排序,用flag判断是否有重复的字符串。
AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <cstring>
#include <cmath>
#include <set>
#include <iterator>
#include <numeric>
using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=1e9+7;
int n;
struct node
{
int time,num;
char c[105];
bool flag;
} e[105];
bool cmp1(node a,node b)
{
if(a.time<b.time) return true;
else return false;
}
int main()
{
// freopen("test.in","r",stdin);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>e[i].c>>e[i].time;
e[i].num=i;
for(int j=1;j<i;j++)
{
if(strcmp(e[j].c,e[i].c)==0&&e[j].time<e[i].time)
e[j].flag=true;
if(strcmp(e[j].c,e[i].c)==0&&e[j].time>e[i].time)
e[i].flag=true;
}
}
sort(e+1,e+1+n,cmp1);
for(int i=1;i<=n;i++)
{
if(e[i].flag==false)
cout<<e[i].num<<'\n';
}
return 0;
}
D - 机器人
思路: 数据范围很小,直接模拟。
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=1e9+7;
const int N=13;
int n,k;
int a[N][N];
bool check()
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if((i-1)*n+j!=a[i][j])
return 0;
}
}
return 1;
}
void f(){
int t=a[1][1];
for(int i=2;i<=n;i++) a[1][i-1]=a[1][i];
for(int i=2;i<=n;i++) a[i-1][n]=a[i][n];
for(int i=n-1;i>=1;i--) a[n][i+1]=a[n][i];
for(int i=n-1;i>=1;i--) a[i+1][1]=a[i][1];
a[2][1]=t;
}
int main()
{
// freopen("test.in","r",stdin);
// freopen("output.in", "w", stdout);
scanf("%d",&n);
int flag=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
}
for(int i=1;i<=100;i++)
{
f();
if(check())
{
cout<<"YES";
return 0;
}
}
cout<<"NO";
return 0;
}
os:模拟真的麻烦,但是模拟的能力很重要!
F osu!
思路:
(1)我的思路:在文章中o,s,u的顺序是不一定的,我的做法是先遍历数每个s前o的数量,用数组存一下,作为每一个s的值,再一次遍历u,把每一个u前s的值相加。
AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <cstring>
#include <cmath>
#include <set>
#include <iterator>
#include <numeric>
using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=1e9+7;
string c;
int s;
int mp1[105],mp2[105];
int main()
{
// freopen("test.in","r",stdin);
cin>>s;
cin>>c;
int cnt=0;
for(int i=0;i<s;i++)
{
if(c[i]=='o')
{
++cnt;
}
if(c[i]=='s')
mp1[i]=cnt;
}
for(int i=0;i<s;i++)
{
if(c[i]=='u')
for(int j=0;j<=i;j++)
{
mp2[i]+=mp1[j];
}
}
int sum=0;
for(int i=0;i<s;i++)
{
sum+=mp2[i];
}
cout<<sum<<'\n';
return 0;
}
(2)题解思路:对于每个s,对于答案的贡献是它左边的o的数量乘它右边u的数量,o和s的数量分别用前缀和和后缀和可以很轻松的O(n)预处理。(没有题解代码,将就看看我写的吧,不过这样想确实要好写许多)
AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <cstring>
#include <cmath>
#include <set>
#include <iterator>
#include <numeric>
using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=1e9+7;
string c;
int co[105],cu[105],cs[105];
int n,sum;
int main()
{
// freopen("test.in","r",stdin);
cin>>n;
cin>>c;
if(c[0]=='o')
co[0]=1;
for(int i=1;i<n;i++)
{
if(c[i]=='o')
co[i]=co[i-1]+1;
else
co[i]=co[i-1];
}
for(int i=n-1;i>=0;i--)
{
if(c[i]=='u')
cu[i]=cu[i+1]+1;
else
cu[i]=cu[i+1];
}
for(int i=0;i<n;i++)
{
if(c[i]=='s')
cs[i]=co[i]*cu[i];
sum+=cs[i];
}
cout<<sum<<'\n';
return 0;
}
G 拼牛牛
废话可跳过: 做这个题的时候就是比赛后半段时间了,当时有点慌了,感觉是暴力可以过,但也没仔细想怎么暴力做,,,如果是高中的我可能就做出来了,现在连等比数列都忘了(fw)。事实证明暴力可以过的。
思路:
(1)科学做法:函数收敛性相关 可以明显看出增长数字是一个等比数列,根据等比数列知道,从别人处得到的钱数为1*(k^0+k^1+……+k^(n-1)),求和为1*(1-k^n)/(1-k),根据极限可求得p每一个值时极限的n,因为k为一位小数,逐个计算即可,代码实现类似打表。
AC代码(没有代码,将就看看我写的吧):
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <cstring>
#include <cmath>
#include <set>
#include <iterator>
#include <numeric>
using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=1e9+7;
int t;
int n;
double k;
int main()
{
// freopen("test.in","r",stdin);
cin>>t;
while(t--)
{
cin>>n>>k;
if(k<=0.5&&n>=99)
cout<<"YES"<<'\n';
else if(k==0.6&&n>=98)
cout<<"YES"<<'\n';
else if(k==0.7&&n>=97)
cout<<"YES"<<'\n';
else if(k==0.8&&n>=96)
cout<<"YES"<<'\n';
else if(k==0.9&&n>=91)
cout<<"YES"<<'\n';
else
cout<<"NO"<<'\n';
}
return 0;
}
(2)暴力做法:就是自己写个求k的各个次方求和的程序,粗略估计范围,不过会有精度差距需注意(不推荐)。
I 史莱姆
思路:这个思路就比较简单了,分裂到最后每个史莱姆的生命值一定是质数才无法分解,分解给出的n的质因数即可(注意n==1时的特判!)
AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <cstring>
#include <cmath>
#include <set>
#include <iterator>
using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define maxn 10000005
#define INF 0x3f3f3f3f
const int mod=1e9+7;
//多组输入记得每次循环之前初始化(数组,栈,队列,某些变量)!!!
const int N=1000005;
int prime[N],m[N];
bool mark[N];
int tot;
int main()
{
int i,j;
tot=0;
mark[0]=mark[1]=1;
for(i=2;i<=100005;i++)
{
if(!mark[i])
prime[++tot]=i;
for(j=1;j<=tot;j++)
{
if(i*prime[j]>100005)
break;
mark[i*prime[j]]=1;
if(i*prime[j]==0)
break;
}
}
int b,n;
int cnt=0;
scanf("%d",&n);
if(n==1)
cout<<"1"<<'\n';
else
{
b=n;
for(int i=1;i<=tot;i++)
{
if(b<prime[i])
break;
while(b%prime[i]==0)
{
m[++cnt]=prime[i];
b=b/prime[i];
}
}
cout<<cnt<<'\n';
}
return 0;
}
L 歪脖子树下的灯
废话可跳过:一开始看的时候列了几个情况试图找规律,然后失败了。。。
思路:
(1)二项分布:由于每次开关都不会相互影响,那么这题符合n次独立重复的伯努利试验。所以可以使用二项分布公式来计算,但是由于此题的特殊性,即所给p是状态改变的概率,改变奇数次则一定最后一次为亮,改变偶数次则一定为暗,故不能全取,只能取奇数项。(化简我和室友算了半个多小时才整出来。。。)
化简过程:
AC代码(用化简的这个式子很好实现,也是自己写的,莫得答案代码):
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <cstring>
#include <cmath>
#include <set>
#include <iterator>
#include <numeric>
using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=1e9+7;
int t;
int n;
double p;
double pmod(double a,int b)
{
double res=1;
while(b)
{
if(b%2==1)
res=res*a;
b/=2;
a=a*a;
}
return res;
}
int main()
{
// freopen("test.in","r",stdin);
cin>>t;
while(t--)
{
scanf("%d",&n);
scanf("%lf",&p);
double a=pmod(1-2*p,n);
double s=(1-a)/2;
printf("%.6lf\n",s);
}
return 0;
}
(2)递推做法:设f [ i ]为第i次拉后,灯为亮着的概率。已知f [ 0 ] = 0,求f [ i ]。
f [ i ] = (1 - p) * f [ i - 1 ] + p * (1 - f [ i - 1 ]) = (1 - 2 * p) * f [ i - 1 ] + p,直接带公式算即可。(公式怎么出来的?代几组试试,找规律吧)
AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <cstring>
#include <cmath>
#include <set>
#include <iterator>
#include <numeric>
using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=1e9+7;
const int N=105;
int t;
int n;
double p;
double f[N];
int main()
{
// freopen("test.in","r",stdin);
cin>>t;
while(t--)
{
scanf("%d",&n);
scanf("%lf",&p);
f[0]=0;
for(int i=1;i<=n;i++)
{
f[i]=(1-2*p)*f[i-1]+p;
}
printf("%.6lf\n",f[n]);
}
return 0;
}
这个补题拖了好久啊
若有错误请指教orzorz