Contest3370 - 2024寒假集训 进阶训练赛 (二)

问题 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),最终总数为\sum_{i=0}^{i=min(n-1,k)} 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;
}

  • 53
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值