问题 A: 过滤多余的空格
题目描述
一个句子中也许有很多个连续空格,过滤掉多余的空格,只留下一个空格。
输入
一行,一个字符串(长度不超过200),句子的头和尾都没有空格。
输出
过滤之后的句子
样例输入
Hello world.This is c language.
样例输出
Hello world.This is c language.
思路分析
当该位置与前一个位置均为空格时,不输出,其余正常输出即可
AC代码
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
string s;
getline(cin,s);//因为有空格,直接cin无法读入空格
int n=s.size();
cout<<s[0];
for(int i=1;i<n;i++)
{
if(s[i-1]==' '&&s[i]==' ') continue;//该位与前一位均为空格,不输出
else
cout<<s[i];
}
cout<<endl;
}
问题 B: 单词反转
题目描述
输入一个句子(一行),将句子中的每一个单词翻转后输出。
输入
只有一行,为一个字符串,不超过500个字符。单词之间以空格隔开
输出
翻转每一个单词后的字符串,单词之间的空格需与原文一致。
样例输入
hello world
样例输出
olleh dlrow
思路分析
分隔每一个单词,反转后输出,空格正常输出
AC代码
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
string s;
getline(cin,s);
int n=s.size();
string s0="";
for(int i=0;i<n;i++)
{
if(s[i]==' ')//找到空格 ,将s0反转,输出,赋值为空
{
reverse(s0.begin(),s0.end());
cout<<s0<<" ";
s0="";
}
else
s0+=s[i];
}
//由于末尾可能不是空格,可能造成最后一个单词无法输出的情况,再进行一次反转操作
reverse(s0.begin(),s0.end());
cout<<s0<<endl;
cout<<endl;
}
问题 C: 确定进制
题目描述
6*9=42对于十进制来说是错误的,但是对于13进制来说是正确的。即, 6(13)* 9(13)= 42(13), 而 42(13)=4*131+2*130=54(10)。
你的任务是写一段程序,读入三个整数p、q和 r,然后确定一个进制 B(2≤B≤40) 使得 p * q = r。 如果 B 有很多选择, 输出最小的一个。
例如:p=11, q=11, r=121.则有11(3)* 11(3)= 121(3)因为 11(3)= 1 * 31+ 1 * 30= 4(10)(因为二进制下是不存在2这个数的,所以最小答案是3而不是2)
和121(3)=1*32+2*31+1*30=16(10)。对于进制 10,同样有11(10)* 11(10)= 121(10)。这种情况下,应该输出 3。如果没有合适的进制,则输出 0。
输入
一行,包含三个整数p、q、r。 p、q、r的所有位都是数字,并且1 ≤ p、q、r ≤ 1,000,000。
输出
一个整数:即使得p*q=r成立的最小的B。如果没有合适的B,则输出0。
样例输入
6 9 42
样例输出
13
思路分析
遍历。寻找三个数都能转化为该进制下,并满足乘法关系的最小进制
AC代码
#include<iostream>
#include<algorithm>
using namespace std;
string p,q,r;
//将x转化成jz进制下的数
int jinzhi(string x,int jz)
{
int n=x.size();
int ans=0;
int p=1;
for(int i=n-1;i>=0;i--)
{
int y=x[i]-'0';
if(y>=jz)
return -1;//不能转化成该进制
ans+=y*p;
p*=jz;
}
return ans;
}
int solve()
{
for(int i=2;i<=40;i++)
{
int x,y,z;
//将x,y,z全转化为i进制
x=jinzhi(p,i);
y=jinzhi(q,i);
z=jinzhi(r,i);
if(x!=-1&&y!=-1&&z!=-1&&x*y==z)//可转化且满足相乘关系
return i;
}
return 0;
}
int main()
{
cin>>p>>q>>r;
int t=solve();
cout<<t<<endl;
}
问题 D: Vigenere密码
题目描述
有一位法国外交家发明了一种神奇的算法明文是M,密钥是k,密文是C。加密规则如下:
c[i]=m[i]@k[i]
加密注意点;
1、加密时忽略明文和密钥的大小写,并在密文中保持明文的大小写
2、当明文M的长度大于密钥K的长度时,K可以被循环使用。
例如,明文 M=HelloworldM=Helloworld ,密钥 k=abck=abc 时,密文 C=HfnlpyosndC=Hfnlpyosnd
输入
输入共2行。
第一行为一个字符串,表示密钥k,长度不超过100,其中仅包含大小写字母。第二行为一个字符串,表示经加密后的密文,长度不超过1000,其中仅包含大小写字母。
输出
输出共1行,一个字符串,表示输入密钥和密文所对应的明文。
样例输入
CompleteVictory
Yvqgpxaimmklongnzfwpvxmniytm
样例输出
Wherethereisawillthereisaway
思路分析
根据加密规则可得关系:(C+26-K)%26=M
AC代码
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
string c,k;
cin>>k>>c;
int mc=c.size();
int mk=k.size();
int j=0;
for(int i=0;i<mc;i++)
{
int C,K,M;
if(c[i]>='a'&&c[i]<='z')
C=c[i]-'a'+1;
else
C=c[i]-'A'+1;
if(k[j]>='a'&&k[j]<='z')
K=k[j]-'a'+1;
else
K=k[j]-'A'+1;
M=(C+26-K)%26;//求解明文
char cc;
//与密文对应大小写相等
if(c[i]>='a'&&c[i]<='z')
cc=M+'a';
else
cc=M+'A';
cout<<cc;
j=(j+1)%mk;//密钥循环重复使用
}
cout<<endl;
}
问题 E: 求阶乘之和
题目描述
给出一个正整数n,求:1!+2!+3!+...+n!的值
输入
一个正整数n,1<=n<=10
输出
一个正整数,即1!+2!+3!+...+n!的值
样例输入
2
样例输出
3
思路分析
递归求阶乘
AC代码
#include<iostream>
#include<algorithm>
using namespace std;
int jc(int x)
{
if(x==1)
return 1;
return x*jc(x-1);
}
int main()
{
int n;
cin>>n;
int su=0;
for(int i=1;i<=n;i++)
{
su+=jc(i);
}
cout<<su<<endl;
return 0;
}
问题 F: 判断是否为素数
题目描述
写一个判断素数的函数,输入一个数,判断它是否为素数,是输出yes,不是输出no
输入
一个正整数n,2<=n<=10000000
输出
n是素数输出yes,不是素数输出no
样例输入
2
样例输出
yes
AC代码
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
bool ss(int x)
{
if(x==2||x==3)
return true;
if(x==4)
return false;
int p=sqrt(x);
for(int i=2;i<=p;i++)
{
if(x%i==0) return false;
}
return true;
}
int main()
{
int n;
cin>>n;
if(ss(n))
cout<<"yes"<<endl;
else
cout<<"no"<<endl;
return 0;
}
问题 G: [蓝桥杯2019初赛]质数
题目描述
求第2019个质数并输出
AC代码
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
bool ss(int x)
{
if(x==2||x==3)
return true;
if(x==4)
return false;
int p=sqrt(x);
for(int i=2;i<=p;i++)
{
if(x%i==0) return false;
}
return true;
}
int main()
{
int nu=0;
for(int i=2;;i++)
{
if(ss(i))
nu++;
if(nu==2019)
{
cout<<i<<endl;
break;
}
}
return 0;
}
问题 H: Bob和Alice(3)
题目描述
求的是最大值!!!
Bob和Alice在玩一个序列游戏
有一个n-1个数的序列
B1 B2……Bn-1
Bob又定义了一个新的序列
A1 A2……An
对每一个i [1 <= i <= n - 1]
Bi>=max(Ai,Ai+1)
好奇的Alice想知道A序列在这样的约束之下
A序列的总和的最大值是多少
输入
n
B1 B2……Bn-1
2 <= n <= 100
0 <= Bi <= 105
输出
A序列的总和的最大值是多少
样例输入
6
0 153 10 10 23
样例输出
53
思路分析
由题可得:
0<=ai<=bi-1;
0<=ai<=bi;
可得:ai=min(bi,bi-1),a0=b0,an=bn-1;
AC代码
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
int main()
{
int n;
cin>>n;
int b[105];
for(int i=1;i<n;i++)
cin>>b[i];
int su=0;
su+=b[1]+b[n-1];
for(int i=2;i<n;i++)
{
su+=min(b[i],b[i-1]);
}
cout<<su<<endl;
return 0;
}
问题 I: 蹩脚两轮车
题目描述
小L设计了一款两轮车,它可以分别对左右轮进行上下左右(UDLR)四种操作。
因为这是他第一次设计两轮车,出现了一些小问题:左轮不能左转,右轮不能右转,其他三个操作是OK的。
为了解决这个毛病,小L自作聪明,想出了一套奇怪的解决机制:当输入一连串的指令后,奇数位上的指令由左轮处理,偶数位上的指令由右轮处理。
注意,这里的下标默认为从1开始。
给出一串指令,由你来判断它是否合法。
输入
一串由UDLR四种字符组成的字符串,分别代表上下左右,四种操作指令。
字符串长度不超过100。
输出
如果指令合法,输出"Yes",否则输出"No"
样例输入
RDULULDURURLRDULRLR
样例输出
Yes
思路分析
注意下标从1开始即可
AC代码
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
string s;
bool ll()
{
int n=s.size();
for(int i=1;i<n;i++)
{
if(i%2==1&&s[i]=='L')
return false;
if(i%2==0&&s[i]=='R')
return false;
}
return true;
}
int main()
{
cin>>s;
s=' '+s;//保证下标从1开始
if(ll())
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
return 0;
}
问题 J: 孙权点兵,多多乱点
题目描述
孙权人称孙十万,有自己独特的对士兵的检验方法。他总是安慰自己:“生子当如孙仲谋”。这次,他来到兵营,看到徐盛和周泰,说:“我来看看你们练兵及不及格。”
孙权随便抽了一组,这一组有n个兵,每个兵的能力值都不一样,也可以说每个兵的能力值是一个n的排列。如果对于一个士兵来说,如果他的能力值对于他旁边的两个士兵来说既不是最低也不是最高,那么这个士兵就是及格的。
求这一组士兵中的及格个数。
所有输入都是整数
3<=n<=20
输入的n个数为1个排列
输入
n
p1 p2 p3....pn
输出
及格人数
样例输入
9
9 6 3 2 5 8 7 4 1
样例输出
5
思路分析
遍历1-(n-2)判断是否符合条件
AC代码
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
int main()
{
int n;
cin>>n;
int a[25];
for(int i=0;i<n;i++)
cin>>a[i];
int su=0;
for(int i=1;i<n-1;i++)
{
if((a[i]>=a[i-1]&&a[i]>=a[i+1])||(a[i]<=a[i-1]&&a[i]<=a[i+1]))//反面判断更方便
continue;
else
su++;
}
cout<<su<<endl;
return 0;
}
问题 K: 许攸说
题目描述
不过现在让我们来看看他是怎么帮曹操夜袭乌巢的。曹操有n个骑兵团,每一个骑兵团的耐力为d,许攸对曹操说,需要找到一个值k,耐力小于k的去乌巢东边偷袭,耐力值大于等于k的去西边偷袭,聪明的你知道有多少种取值k能让去偷袭东边的骑兵团数量和去偷袭西边的骑兵团数量相等吗?
2<=n<=1e5
1<=di<=1e5
输入全为整数
输入
n
d1 d2 d3.. dn
输出
多少种选择
样例输入
6
9 1 4 4 6 7
样例输出
2
思路分析
若n为奇数:没有符合条件的选择
若n为偶数:前1-n/2去东边,后n/2-n去西边,选择数即为大小在中间的两个数的差值
AC代码
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
int a[100005];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
if(n%2)
{
cout<<"0"<<endl;
return 0;
}
sort(a,a+n);
cout<<a[n/2]-a[(n/2)-1]<<endl;
return 0;
}
问题 L: 两点之间距离是整数的有多少呢?
题目描述
给定d维空间上的n个点
你需要计算出有多少点对(i,j) i < j
满足条件:2点之间的距离为整数
d维空间中点(x1,x2,.....xd) 到 点(y1,y2,.....yd)的距离为 (x1 - y1) * (x1 - y1) + .... + (xd - yd) * (xd - yd) 的和再开根号
聪明你的可以计算出来答案吗
输入
n d
X11 X12 .....X1d
X21 X22 .....X2d
.....
Xn1 Xn2 .....Xnd
1 <= n <= 10
1 <= d <= 20
-20 <= Xij <= 20
输出
输出一个数
有多少对点对满足题意
样例输入
3 2
1 2
5 5
-2 8
样例输出
1
思路分析
暴力。遍历任意两个点之间的距离,求根后的值的平方是否与原来相等
AC代码
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
int main()
{
int n,d;
cin>>n>>d;
int a[15][25];
for(int i=0;i<n;i++)
{
for(int j=0;j<d;j++)
cin>>a[i][j];
}
int sum=0;
for(int i=0;i<n-1;i++)
{
for(int j=i+1;j<n;j++)
{
int su=0;
for(int k=0;k<d;k++)
{
su+=(a[i][k]-a[j][k])*(a[i][k]-a[j][k]);
}
int p=sqrt(su);
if(p*p==su)
sum++;
}
}
cout<<sum<<endl;
return 0;
}
问题 M: 珠宝
题目描述
你的朋友送了你一个序列D
这个序列D上一共有n个珠宝
按照从左到右的顺序
每个珠宝都有它的价值w[i]
1 <= n <= 50
-1e7 <= w[i] <= 1e7
你可以对这些珠宝进行 最多 k次操作
1 <= k <= 100
每次操作你都可以
操作A:取出D中最左边的宝石,放在手中。当D为空时,不能执行此操作。
操作B:取出D中最右边的宝石,放在手中。当D为空时,不能执行此操作。
操作C:选择手中的任意一个宝石并将其插入D的左端。如果手中没有宝石,则无法执行此操作。
操作D:选择手中的任意一个宝石并将其插入D的右端。如果手中没有宝石,则无法执行此操作。
求执行完 最多k次操作之后 你手上有的珠宝的价值之和的最大值
输入
n k
w1 w2 ...... wn
输出
打印一行
执行完 最多k次操作之后 你手上有的珠宝的价值之和的最大值
样例输入
6 4
-10 8 2 1 2 6
样例输出
14
思路分析
左边拿i个,右边拿n-j个,如果拿的个数超过k了,结束此次循环;否则若还有次数可用并且手中有负值,将负值放回,每次更新最大值
AC代码
#include<iostream>
#include<algorithm>
#include<math.h>
#include<set>
using namespace std;
int main()
{
int n,k;
cin>>n>>k;
int a[55];
for(int i=0;i<n;i++)
cin>>a[i];
int sum=0;
for(int i=0;i<n;i++)//左边拿0-i
{
for(int j=n;j>=i;j--)//右边拿j-(n-1)
{
if(i+n-j>k) continue;
multiset<int>mse;
int su=0;//目前总价值
for(int k=0;k<i;k++)
{
mse.insert(a[k]);
su+=a[k];
}
for(int k=n-1;k>=j;k--)
{
mse.insert(a[k]);
su+=a[k];
}
int p=k-i-n+j;
while(p>0&&*mse.begin()<0)//还有次数可用并且目前手里有负值
{
su-=*mse.begin();
mse.erase(mse.begin());
p--;
}
sum=max(sum,su);
}
}
cout<<sum<<endl;
return 0;
}
问题 N: 道路工程
题目描述
有一条从西到东的无限长的街道,我们认为这是一条无限长的数轴
这条街上计划进行N项道路工程。
第i个道路工程在坐标Xi处阻段该点 阻挡的时间从Si - 0.5 到 Ti - 0.5
也就是说在 Si - 0.5 到 Ti - 0.5 这段时间内 Xi 这个点不能通过
第i个人将在时间Di开始从坐标0 , 不断以速度1个单位每秒正向行走,到达阻段点时停止行走。
求出每个人将要走的距离
如果这个人走的过程中不会遇到阻断点
则输出-1
1 <= N , Q <= 2e5 (200000)
0 <= Si < Ti <= 1e9 (1000000000)
1 <= Xi <= 1e9
0 <= D1 < D2 < D3 < ....... < Dq <= 1e9
输入
N Q
S1 T1 X1
S2 T2 X2
...........
Sn Tn Xn
D1
D2
.....
Dq
输出
每行一个数
输出第i个人将要走的距离
如果这个人走的过程中不会遇到阻断点
则输出-1
样例输入
4 6
1 3 2
7 13 10
18 20 13
3 4 2
0
1
2
3
5
8
样例输出
2
2
10
-1
13
-1
思路分析
将x位置的修路时间映照到如果是在0位置的修路时间,若某人出发时间在该点映照到0位置修路时间段内,则无法通过,寻找最小的位置。暴力会超时。
按位置从小到大依次遍历修路的位置,寻找出发时间大于修路开始时间的人,若在该时间段内,则走的距离为该位置
lower_bound(begin,end,num),寻找begin到end段内第一个大于num的数
AC代码
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
int ans[200005];
struct kjd
{
int s,t,x;
}a[200005];
bool cmp(struct kjd p,struct kjd q)
{
return p.x<q.x;
}
int main()
{
int n,q;
cin>>n>>q;
for(int i=0;i<n;i++)
{
scanf("%d%d%d",&a[i].s,&a[i].t,&a[i].x);
//映照到0位置的时间段
a[i].s=a[i].s-a[i].x;
a[i].t=a[i].t-1-a[i].x;
}
sort(a,a+n,cmp);
set<pair<int,int>>mp;
for(int j=0;j<q;j++)
{
int r;
cin>>r;
mp.insert({r,j});
ans[j]=-1;
}
for(int i=0;i<n;i++)
{
int s=a[i].s;
while(!mp.empty())
{
auto it=mp.lower_bound({s,-2e9});//循环找第一个时间比s大的
if(it==mp.end())//找不到
break;
if((it->first)>a[i].t)//不介于修路时间内
break;
ans[it->second]=a[i].x;//第it->second的人第一次卡在i位置处,故所走距离为a[i].x
mp.erase(it);
}
}
for(int i=0;i<q;i++)
cout<<ans[i]<<endl;
return 0;
}
问题 P: 请勿水群
题目描述
在某正经群里,管理员对那些无意义的灌水消息感到厌烦。因此,他想了一个办法来设置屏蔽词。
他截取了群里的 n 条消息记录,如果某条消息出现的次数最多,那么该条消息将会被纳入屏蔽词列表中。
请你设计一个程序,给你这 n 条消息记录,请列出所有的屏蔽词,并按照字典序由小到大输出。
输入
输入第一行,一个整数n ,代表消息数量。(1≤n≤2×105)
接下来 n 行,每行一个字符串 Si。
保证每个Si只包含小写字母,并且长度介于[1,10].
输出
按照字典序由小到大,将屏蔽词依次输出,每行一个。
样例输入
7
bda
abd
abc
abd
hhhhhhha
abc
bda
样例输出
abc
abd
bda
提示
样例输入2
4 p p a p
样例输出2
p
思路分析
寻找消息最多的数目为ma,然后依次遍历每个消息,若数量等于ma则输出
map容器可将消息按字典序排好
AC代码
#include<iostream>
#include<algorithm>
#include<math.h>
#include<map>
using namespace std;
int main()
{
int n;
cin>>n;
map<string,int>mp;
for(int i=0;i<n;i++)
{
string s;
cin>>s;
mp[s]++;
}
int ma=0;
for(auto k:mp)//寻找最多的消息数
{
ma=max(ma,k.second);
}
for(auto k:mp)
{
if(k.second==ma)
cout<<k.first<<endl;
}
return 0;
}
问题 Q: xsyの末日!
题目描述
在遥远的亚马逊雨林里有一种叫 xsy 的昆虫,其中一只 xsy 经过跋山涉水无数天偷摸在 BUCT 繁衍下来。
某一天在 BUCT 某只大鹅的窝里出现了 n 只 xsy,经过多名化学专家苦心钻研研制出一种特效药可以对成群 xsy 进行绝育。
经过实验室计算对 n 只 xsy 进行绝育需要喷洒 m 毫升的特效药。
但负责喷洒的kx哥哥为了节省药物:
当 xsy 的数量小于 10 只时,他就会喷洒 m−(100×(10−n))毫升
当 xsy 数量大于等于 10 时,他就不敢节省而喷洒 m 毫升药
给出 xsy 的数量n和kx哥哥已经喷洒药物的量 q(单位:毫升),请求出未喷洒之前的药物的量(单位:毫升) m
输入
两个整数 n,q
,1≤n≤100,0≤q≤4111
输出
一个整数,代表 m 的值
样例输入
2 2919
样例输出
3719
思路分析
n>=10,m=q;
n<10,m=q+100*(10-n)
AC代码
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
int main()
{
int n,p;
cin>>n>>p;
if(n>=10) cout<<p<<endl;
else cout<<p+(100*(10-n))<<endl;
return 0;
}
问题 R: 重生之我是菜狗
题目描述
xsy:哈嗨!没想到吧,我又回来了
自从上次把 xsy 全绝育了之后,不知道为何 xsy 的种群密度又多了起来
经过调查亚马逊雨林的壁画发现,每逢每天的11:45:14
当一个种群中有 a种颜色 xsy 或 b 种颜色的 xsy 被献祭之后,其他 xsy 就会快速恢复生育能力并且快速繁殖
如果献祭的 xsy 数量不是 a 或 b,那么该种群所有的xsy将会全部暴毙
于是你迫不及待抓了 n 只不同颜色的 xsy 来(每种颜色一只),打算人工献祭以绝后患
也就是说,你可以从 n 只里面任选 x (x>0) 只,只要 x≠a 且 x≠b 即可。
有多少选择方案可以使得这批 xsy 全都暴毙?(对 109+7 取模)
(当有其中一只 xsy 在 A 中没有被献祭,在 B 中被献祭了,称 A 与 B 是不同的方案)
输入
输入三个整数,n,a,b
2≤n≤109
1≤a<b≤min(n,2×105)
输出
输出一个整数,代表答案对 109+7 取模的结果
如果没有任何方案,答案是 0
样例输入
4 1 3
样例输出
7
思路分析
即求n的所有组合数之和-C(n,a)-C(n,b)-C(n,0)
n的所有组合数之和为2的n次方,C(n,0)=1
AC代码
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
int N=1e9+7;
//快速幂求2的n次方
int qpow(int x,int y)
{
int sun=1;
while(y)
{
if(y%2) sun=sun*x%N;
y=y/2;
x=x*x%N;
}
return sun;
}
signed main()
{
int n,a,b;
cin>>n>>a>>b;
int su=qpow(2,n);
su--;
if(su<0) su+=N;
int l=1,r=n,s=1;
for(int i=1;i<=b;i++)
{
//求组合数C(n,i)
s=s*(n-i+1)%N;
s=s*qpow(i,N-2)%N;
if(i==a||i==b)
{
su-=s;
if(su<0) su+=N;
}
l++;
r--;
}
cout<<su<<endl;
return 0;
}
问题 S: 这隔离点网太烂了!
题目描述
在xx山庄有 n 个倒霉的小朋友在 n 个房间正在隔离(其中就包括小L)。
最开始都是每个人连自己房间的 wifi,但是由于这网实在是太烂了,很多同学都会像小L一样换个网:
每次换网,可以将一个同学连接的网从房间 a 更换到房间 b(a≠b,a,b∈[1,n])
请算出经恰好 k 次换网后,这 n 个房间的网络连接有多少种不同的状态
(如果在a方案中第 i 个房间有 x个人连,在b方案种第 i 个房间有 y 个人连接,若存在i≤n且 x≠y,则认为a和b是不同的方案)
输入
输入两个整数,代表 n,k
(3≤n≤2×105,2≤k≤109)
输出
输出一个整数,代表答案有多少种不同的状态,对 109+7 取模的结果
样例输入
3 2
样例输出
10
思路分析
移动k次,最多有min(n-1,k)个空房间,对于i个空房间,挑选出i个空房间的选择数为C(n,i),将n个人放到剩余n-i个房间中去,运用隔板法,即在n-1个空隙内插入n-1-i个隔板,选择数为C(n-1,n-1-i)即为C(n-1,i),则对于i个空房间,选择总数为C(n,i)*C(n-1,i),最终总数为
AC代码
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
int N=1e9+7;
int a[200005],b[200005];
int qpow(int x,int y)
{
int sun=1;
while(y)
{
if(y%2) sun=sun*x%N;
y=y/2;
x=x*x%N;
}
return sun;
}
signed main()
{
int n,k;
cin>>n>>k;
int su=0;
//求组合数预处理
a[0]=1;
for(int i=1;i<=n;i++)
a[i]=a[i-1]*i%N;
for(int i=0;i<=n;i++)
b[i]=qpow(a[i],N-2)%N;
k=min(k,n-1);
for(int i=0;i<=k;i++)
{
int p,q;
p=a[n]*b[n-i]%N*b[i]%N;//C(n,i)
q=a[n-1]*b[n-1-i]%N*b[i]%N;//C(n-1,i)
su=(su+p*q%N)%N;
}
cout<<su<<endl;
return 0;
}