比赛链接:https://www.nowcoder.com/acm/contest/96#question
A | LL |
题意:输入一行字符串,如果恰好是lovelive(不区分大小写)就输出yes,否则输出no。
记录下Python代码:
while True:
try:
print("yes" if input().lower()=="lovelive" else "no")
except:
break
C | 取手机 |
学的概率都忘了,应该是理解为第几次取对结果是没有影响的,所以可以理解为求第一次取取出s8概率是多少,但是比赛时我算的方法是:先算总共的取法为C(a+b,b),意思就是,理解为有a+b个位置,将a个"1"b个"0"放入这些位置中总共有多少种方案;再求对应k位置为"0",其余位置任意的方案数:C(a+b-1,a),不知道这个方法对不对,倒是得出结果正是b/(a+b)
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int T,a,b,k;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&a,&b,&k);
cout<<fixed<<setprecision(3)<<(1.0*b/(1.0*a+1.0*b))<<endl;
}
return 0;
}
D | zzq的离散数学教室1 |
题意:对于任意两个正整数a和b(a<b)来说,如果b%a==0,并且不存在一个正整数c(a<c<b),使得条件b%c==0和c%a==0同时成立,那么我们就在节点a和节点b之间连一条边。现在问题是,给你们2个数L,R(1<=L,R<=1e6)。[L,R]中不同两个整数之间组成一个整数对,求有多少整数对满足上面的加边条件。
解析:发现对于[l,r]中一个整数x,满足条件的<x,y>中的y一定是x的素数倍并且小于等于R,那么我们可以先筛素数再统计,然而这里我们枚举[l,r]中每一个数作为x再找有几个y是超时的,可以通过反向思维,枚举每个素数prime[i]判断在它可以作为多少个x的倍数。我才开始是想记录到i为止素数的个数t[i],然后直接遍历for(i=L;i<=R;i++) ans+=t[r/i];但遍历次数太多也超时。
代码:
#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<queue>
#include<cstring>
#include<map>
#include<vector>
using namespace std;
typedef long long ll;
#define M 1000001
int l,r,num,t[M+5];
int prime[M+5];
bool vis[M+5];
void init()
{
int m=sqrt(M+0.5);
int i,j;
num=0;
for(i=2;i<=m;i++)
{
if(!vis[i])
{
for(j=i*i;j<=M;j+=i)
{
vis[j]=true;
}
}
}
for(i=2;i<=M;i++)
{
if(!vis[i])
{
prime[num++]=i;
}
}
}
int main()
{
init();
while(scanf("%d%d",&l,&r)!=EOF)
{
int ans=0;
for(int i=0;i<num&&prime[i]<=r;i++)
{
int t=r/prime[i]; //t是满足x*prime[i]<=r的最大x,[1,t]的所有数都可以作为x去乘prime[i]
if(t>=l) ans+=(t-l+1);//这里只加[1,t]与[L,R]交集中的x
}
printf("%d\n",ans);
}
return 0;
}
E | 小木乃伊到我家 |
解析:最短路径问题
#include<bits/stdc++.h>
using namespace std;
const int maxn = 200005;
vector<pair<int,int> >E[maxn];
int n,m;
int dis[maxn],vis[maxn];
void init()
{
for(int i=0;i<maxn;i++) E[i].clear();
for(int i=0;i<maxn;i++) vis[i] = 0;
for(int i=0;i<maxn;i++) dis[i] = 1e9;
}
int main()
{
while(cin>>n>>m)
{
init();
for(int i=0;i<m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
E[x].push_back(make_pair(y,z));
E[y].push_back(make_pair(x,z));
}
queue<int>Q;
Q.push(1);
dis[1] = 0,vis[1] = 1;
while(!Q.empty())
{
int now = Q.front();
Q.pop();vis[now] = 0;
for(int i=0;i<(int)E[now].size();i++)
{
int v = E[now][i].first;
if(dis[v] > dis[now] + E[now][i].second)
{
dis[v] = dis[now] + E[now][i].second;
if(vis[v] == 1) continue;
vis[v] = 0;
Q.push(v);
}
}
}
if(dis[n] == 1e9) printf("qwb baka\n");
else printf("%d\n",dis[n]);
}
}
G | 逃离迷宫 |
解析:先记录每个"K"点信息,用两次bfs,一次求出"P"到所有"K"点的距离,一次求出"E"到所有"K"点的距离,枚举"k"点找最小值,注意两次bfs不同只有在"P"作为起点时不能到"E"和"#",而在"E"作为起点时只是不能为"#"。
代码:
#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<queue>
#include<cstring>
#include<map>
#include<vector>
using namespace std;
typedef long long ll;
#define M 505
#define inf 0x7fffffff
struct node{
int x,y,s;
};
int n,m,xp,yp,xe,ye;
char mp[M][M];
bool vis[M][M];
node kxy[M];
int ans1[M],ans2[M],numk;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
void bfs1()
{
queue<node> Q;
node t1,t2;
t1.x=xp;
t1.y=yp;
t1.s=0;
Q.push(t1);
vis[xp][yp]=true;
while(!Q.empty())
{
t1=Q.front();
Q.pop();
for(int k=0;k<4;k++)
{
t2.x=t1.x+dx[k];
t2.y=t1.y+dy[k];
if(t2.x>=0&&t2.x<n&&t2.y>=0&&t2.y<m&&mp[t2.x][t2.y]!='#'&&mp[t2.x][t2.y]!='E'&&vis[t2.x][t2.y]==false)
{
vis[t2.x][t2.y]=true;
t2.s=t1.s+1;
if(mp[t2.x][t2.y]=='K')
{
//cout<<t2.x<<" "<<t2.y<<endl;
for(int r=0;r<numk;r++)
{
if(kxy[r].x==t2.x&&kxy[r].y==t2.y)
{
ans1[r]=t2.s;
break;
}
}
}
Q.push(t2);
}
}
}
}
void bfs2()
{
queue<node> Q;
node t1,t2;
t1.x=xe;
t1.y=ye;
t1.s=0;
Q.push(t1);
vis[xe][ye]=true;
while(!Q.empty())
{
t1=Q.front();
Q.pop();
for(int k=0;k<4;k++)
{
t2.x=t1.x+dx[k];
t2.y=t1.y+dy[k];
if(t2.x>=0&&t2.x<n&&t2.y>=0&&t2.y<m&&mp[t2.x][t2.y]!='#'&&vis[t2.x][t2.y]==false)
{
vis[t2.x][t2.y]=true;
t2.s=t1.s+1;
if(mp[t2.x][t2.y]=='K')
{
//cout<<t2.x<<" "<<t2.y<<endl;
for(int r=0;r<numk;r++)
{
if(kxy[r].x==t2.x&&kxy[r].y==t2.y)
{
ans2[r]=t2.s;
break;
}
}
}
Q.push(t2);
}
}
}
}
int main()
{
int T,i,j;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
numk=0;
for(i=0;i<n;i++)
{
scanf("%s",mp[i]);
for(j=0;j<m;j++)
{
if(mp[i][j]=='P') {xp=i;yp=j;}
else if(mp[i][j]=='E') {xe=i;ye=j;}
else if(mp[i][j]=='K') {kxy[numk].x=i;kxy[numk].y=j;numk++;}
}
}
for(i=0;i<numk;i++)
{
ans1[i]=ans2[i]=inf;
}
memset(vis,false,sizeof(vis));
bfs1();
memset(vis,false,sizeof(vis));
bfs2();
int ans=inf;
for(i=0;i<numk;i++)
{
if(ans1[i]!=inf&&ans2[i]!=inf)
{
if(ans1[i]+ans2[i]<ans)
ans=ans1[i]+ans2[i];
}
}
if(ans!=inf)
cout<<ans<<endl;
else
cout<<"No solution"<<endl;
}
return 0;
}
H | 数学考试 |
题意:一共有n道题,每道题能获得的分数为ai,但并不打算把这些题全做完,要选总共2*k道题来做,并且期望能获得的分数尽可能的大,准备选2个不连续的长度为k的区间,即[L,L+1,L+2,....,L+k-1],[R,R+1,R+2,...,R+k-1](R >= L+k)。使得这些题目总分和最大。
解析:这道题是优化了好几次才过的,可能是dp但是我dp不怎么样,具体方法见代码
代码:
#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<queue>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
#define M 200005
int n,k,a;
ll sum[M],ans[M],Max[M];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&k);
for(int i=0;i<=n;i++)
{
Max[i]=-99999999999999999;
sum[i]=ans[i]=0;
}
cin>>sum[1];
for(int i=2;i<=n;i++) //sum[i]是前i道题目的分数前缀和
{
scanf("%d",&a);
sum[i]=sum[i-1]+a;
}
for(int i=1;i<=n-k+1;i++)//ans[i]是以i为起点的k道题目的分数和,Max[i]是到当前为止最大的一个分数和
{
ans[i]=sum[i+k-1]-sum[i-1];
Max[i]=max(Max[i-1],ans[i]);
}
ll res=-99999999999999999;
for(int i=k+1;i<=n-k+1;i++)
{
//cout<<"ans["<<i<<"]="<<ans[i]<<" "<<"Max["<<i-k<<"]="<<Max[i-k]<<endl;
res=max(res,ans[i]+Max[i-k]);//(当前段分数和ans[i]+前面段最大的分数和Max[i-k])的最大值即是答案,注意是[i-k]
}
cout<<res<<endl;
}
return 0;
}
J | 杯子 |
题意:有n个球编号1~n,要依次,也就是按照1,2,3...n的顺序放进杯子里,然后在全部拿出来(注意不一定要等到全部放进去才能拿出球),并且会记录放进和拿出球的顺序,想知道,要满足当第m个球进去后,杯子中此时恰好有k个球,然后仍然要把剩下的n-m个球放进去,最后杯中的球要取光,这样的放进和拿出球的顺序有多少种,
解析:很经典的进栈出栈问题,用卡特兰数来解。首先卡特兰数等于C(n+m,n)-C(n+m,m-1),它的一个解释为有有n个"1"、m个"-1"(n>=m),共n+m个数排成一列,满足对所有0<=k<=n+m的前k个数的部分和Sk >= 0的排列数。 问题等价为在一个格点阵列中,从(0,0)点走到(n,m)点且不穿过对角线x==y的方法数(可以走到x==y的点)。(具体见百度百科https://baike.baidu.com/item/%E5%8D%A1%E7%89%B9%E5%85%B0%E6%95%B0/6125746?fr=aladdin)。
对于此题,设入栈为“1”出栈为“-1”,①.对于放置第m个球之前一共有入栈m-1次,出栈m-k次,求进栈大于等于出栈的方案数就是C(m-1+m-k,m-1)-C(m-1+m-k,m-k-1)
②.在放完第m求之后,还需要有入栈n-m次,出栈n-m+k次,这里要求是出栈大于等于进栈的方案数,C(n-m+n-m+k,n-m)-C(n-m+n-m+k,n-m+k-1)
最终答案就是C(m-1+m-k,m-1)-C(m-1+m-k,m-k-1)*C(n-m+n-m+k,n-m)-C(n-m+n-m+k,n-m+k-1)
代码:
#include<bits/stdc++.h>
#define inf 80000000000
#define ll long long
using namespace std;
const int maxn = 1e6+2;
const ll mod = 1e9+7;
ll jie[maxn*2];
int n,m,k,T;
ll pow_mod(ll a,ll b) //快速幂计算(a^b)%mod
{
ll ret = 1;
while(b)
{
if(b&1) ret = ret%mod*a%mod%mod;
a = a%mod*a%mod%mod;
b>>=1;
}
return ret;
}
void init() //计算阶乘
{
jie[0] = jie[1] = 1;
for(int i = 1;i<=2*maxn;i++){
jie[i] = jie[i-1]%mod*i%mod%mod;
}
}
ll C(ll n,ll m) //计算C(n,m)
{
if(m<0) return 0;
return (jie[n]%mod*pow_mod(jie[n-m]%mod*jie[m]%mod%mod,mod-2)%mod)%mod;//用快速幂求逆元就是将(a/b)%mod换为a*(1/b)%mod,这里(1/b)=pow_mod(b,mod-2)%mod
}
ll Cal(ll n,ll m) //计算卡特兰数=C(n+m,n)-C(n+m,m-1)
{
return (C(n+m,n)%mod-C(n+m,m-1)%mod+mod)%mod;
}
int main()
{
scanf("%d",&T);
init();
while(T--)
{
scanf("%d%d%d",&n,&m,&k);
if(m<k){
puts("0");
continue;
}
printf("%lld\n",Cal(n-m+k,n-m)%mod*Cal(m-1,m-k)%mod%mod);
}
}
L | 仓鼠养殖计划 |
代码:
#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<queue>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
#define M 55
int a,b,n,p[M];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&a,&b,&n);
for(int i=0;i<n;i++)
scanf("%d",&p[i]);
bool f=true;
for(int i=0;i<n;i++)
{
while(p[i]>=2)
{
if(b>=1)
{
p[i]-=2;
b--;
}else{
break;
}
}
while(p[i]>=1)
{
if(a>=1)
{
p[i]--;
a--;
}else{
break;
}
}
if(p[i])
{
f=false;
break;
}
}
if(f)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
return 0;
}