填空题目来源来自于:https://blog.csdn.net/l503301397/article/details/90697079
大题来源于:ACwing https://www.acwing.com/problem/search/2/?csrfmiddlewaretoken=FZ3nfW3ve6OUKD4s75NP4EmknhbU2HMXthYBpJjyLLuNijuhPQDekbRmGr8Sc38s&search_content=%E8%93%9D%E6%A1%A5%E6%9D%AF
不一定都对,欢迎大佬们指出错误
1、2019
满足条件的X和Y可能有多种情况,请给出X+Y的值,并且令X+Y尽可能的小。
答案:7020
分析:暴力
代码:
#include
#include
#include
using namespace std;
typedef long long ll;
int main ()
{
//freopen("D:\\input.txt", "r", stdin);
//freopen("D:\\output.txt", "w", stdout);
cin.tie(0),ios::sync_with_stdio(false);
for(ll i=2020;;i++){
ll cha=i*i-2019*2019;
ll j=i*i+cha;
if(sqrt(j)*sqrt(j)==j){
cout<<i+sqrt(j)<<endl;
break;
}
}
return 0;
}
2019可以被分解成若干个两两不同的素数,请问不同的分解方案有多少种?
注意:分解方案不考虑顺序,如2+2017=2019和2017+2=2019属于同一种方案。
答案:55965365465060
分析:题目中说的若干个,那么这就是一个01背包问题,直接用dp。
dp[i][j]表示前i个素数和为j时的方法数。对于第i个素数可以选或不选,每个那么状态转换方程就是:不选 dp[i][j] = dp[i-1][j];选:dp[i][j] += dp[i-1][j-a[i]](j >= a[i])。
代码:
#include
#include
using namespace std;
const int maxn=5e5+7;
typedef long long ll;
int vis[maxn];
int a[maxn];
ll dp[2020][2020]={
1};
int isprime(int x){
for(int i=2;i<=sqrt(x);i++){
if(x%i==0)
return 0;
}
return 1;
}
int main ()
{
//freopen("D:\\input.txt", "r", stdin);
//freopen("D:\\output.txt", "w", stdout);
cin.tie(0),ios::sync_with_stdio(false);
int cnt=0;5
int n;
cin>>n;
for(int i = 2; i <= 2017 ;i++) {
if(isprime(i)){
vis[i] = 1;
a[++cnt] = i;
}
}
for(int i = 1; i <= cnt ;i++) {
for(int j = 0; j <= n ;j++){
dp[i][j] = dp[i-1][j];
if( j >= a[i]) {
dp[i][j] += dp[i-1][j-a[i]];
}
}
}
cout<<dp[cnt][n]<<endl;
return 0;
}
7×7方格,分成两部分,每部分连通,右半部分翻转旋转拼接之后也是7×7,
有多少种分割方法
答案:45406
分析:评论区有大佬指出了错误,在每一个点都可以向四个方向减,那么这也就是是一个搜索的问题。可以看出要让右边旋转可以仍然和左边拼成正方形,那么就需要剪出来右边和左边都是轴对称的。
既然剪出来的是对称的们就可以只考虑左半边怎么选择减的路径,答案应该是有多少种路径可以减到对角线上。
例如从(0,0)开始剪如图可见,有7处需要选择剪得得方向。开始剪的点是(0,0)。最后还要减去从起点出发一直向下或向左剪是一条直线的情况,答案就是45408-2=45406。
#include
using namespace std;
int vis[10][10];
int dir[4][2] = {
1,0,-1,0,0,1,0,-1};
int ans=0;
void dfs(int x,int y)
{
if( x + y == 7 ) {
ans++;
return ;
}
for(int i = 0 ;i < 4; i++) {
int xx = x + dir[i][0];
int yy = y + dir[i][1];
if( xx+yy > 7||xx<0||xx>7 || yy<0 ||yy>7||vis[xx][yy])
continue;
vis[xx][yy] = 1;
dfs(xx,yy);
vis[xx][yy] = 0;
}
}
int main()
{
vis[0][0] = 1;
dfs(0,0);
vis[0][0] = 0;
cout<< ans<<endl;
return 0;
}
有一个7X7的方格。方格左上角顶点坐标为(0,0),右下角坐标为(7,7)。
求满足下列条件的路径条数:
1、起点和终点都是(0,0)
2、路径不自交
3、路径长度不大于12
4、对于每一个顶点,有上下左右四个方向可以走,但是不能越界。
例如,图中路线,左上角顶点(0,0),路线长度为10
答案:206
分析:dfs,路径一定大于2,剪枝路径>12的,以及x和y坐标和大于6时,一定不满足,结束搜索。
代码:
#include
#include
#include
using namespace std;
typedef long long ll;
int vis[10][10]={
0};
int dir[4][2]={
1,0,-1,0,0,1,0,-1};
int ans = 0;
int check(int x,int y){
if(0 <= x && x <= 7 && y >= 0 && y <= 7)
return 1;
else
return 0;
}
void dfs(int x,int y,int cnt)
{
if(x+y > 7 || cnt > 12)
return;
for(int i = 0; i <4 ;i++){
int nx = x+dir[i][0];
int ny = y+dir[i][1];
if(nx == 0&&ny == 0&& cnt+1 >2&&cnt+1 <= 12)
ans++;
if(vis[nx][ny] == 1||check(nx,ny) == 0)
continue;
vis[nx][ny] = 1;
dfs(nx,ny,cnt+1);
vis[nx][ny] = 0;
}
return ;
}
int main ()
{
//freopen("D:\\input.txt", "r", stdin);
//freopen("D:\\output.txt", "w", stdout);
cin.tie(0),ios::sync_with_stdio(false);
vis[0][0]=1;
dfs(0,0,0);
cout<<ans<<endl;
return 0;
}
有1个约数的最小数为1(1),有两个约数的最小数为2(1,2)……
有n个约数的最小数为Sn
S1=1 (1)
S2=2 (1 2)
S3=4 (1 2 4)
S4=6 (1 2 3 6)
求S100
答案:45360
分析:反正是个填空题,直接暴力就完了…
#include
#include
#include
using namespace std;
typedef long long ll;
set<int>vis;
int slove(int x){
vis.clear();
int cnt =0;
for(int i = 1; i <= x/2 ;i++) {
if( x%i != 0)
continue;
if(vis.count(i) == 0) {
cnt++;
vis.insert(i);
}
if(vis.count(x/i) == 0) {
cnt++;
vis.insert(x/i);
}
}
return cnt;
}
int main ()
{
//freopen("D:\\input.txt", "r", stdin);
//freopen("D:\\output.txt", "w", stdout);
cin.tie(0),ios::sync_with_stdio(false);
int ans=0;
for(int i = 100; ;i++) {
cout<<i<<" "<<slove(i)<<endl;;
if(slove(i) == 100) {
cout<<i<<endl;
break;
}
}
system("pause");
return 0;
}
题目链接:https://www.acwing.com/problem/content/2555/
我们称一个字符串 S 包含字符串 T 是指 T 是 S 的一个子序列,即可以从字符串 S 中抽出若干个字符,它们按原来的顺序组合成一个新的字符串与 T 完全一样。
给定两个字符串 S 和 T,请问最少修改 S 中的多少个字符,能使 S 包含 T?
输入格式
输入两行,每行一个字符串。
第一行的字符串为 S,第二行的字符串为 T。
两个字符串均非空而且只包含大写英文字母。
输出格式
输出一个整数,表示答案。
数据范围
1≤|T|≤|S|≤1000
输入样例:
ABCDEABCD
XAABZ
输出样例:
3
分析:又是一个dp,用dp[i][j]表示s的前i个要包含t的前j个所需要修改的最少字符个数。
那么状态转移方程就是当s[i-1]==t[j-1]时,不需要修改 dp[i][j] = min(dp[i][j],dp[i-1][j-1])
s[i-1]!=t[j-1]时,dp[i][j] = min(dp[i-1][j-1]+1,dp[i-1][j])
前者表示修改当前的第j个字符,后者表示不修改。
需要注意的是因为是求最小,那么dp的初值为INF,在第一次更新时需要特殊处理一下。
代码:
#include
#include
using namespace std;
int dp[1005][1005];
#define INF 0x3f3f3f3f
int main()
{
string s,t;
cin>>s>>t;
memset(dp,INF,sizeof(dp));
for (int i = 1;i <= s.size() ;i++) {
for (int j =1; j <= i ;j++){
if(s[i-1] == t[j-1]){
if(dp[i-1][j-1] == INF) dp[i][j] = min(dp[i][j],0);
else dp[i][j] = min(dp[i][j],dp[i-1][j-1]);
}
else {
if(dp[i-1][j-1]== INF)
dp[i][j] = min(1,dp[i-1][j]);
else dp[i][j] = min(dp[i-1][j-1]+1,dp[i-1][j]);
}
//cout<
}
//cout<
}
cout<<dp[s.size()][t.size()]<<endl;
return 0;
}
题目链接:https://www.acwing.com/problem/content/2556/
在一个排列中,一个折点是指排列中的一个元素,它同时小于两边的元素,或者同时大于两边的元素。
对于一个 1∼n 的排列,如果可以将这个排列中包含 t 个折点,则它称为一个 t+1 单调序列。
例如,排列 (1,4,2,3) 是一个 3 单调序列,其中 4 和 2 都是折点。
给定 n 和 k,请问 1∼n 的所有排列中有多少个 k 单调队列?
输入格式
输入一行包含两个整数 n,k。
输出格式
输出一个整数,表示答案。
答案可能很大,你可需要输出满足条件的排列数量除以 123456 的余数即可。
数据范围
1≤k≤n≤500
输入样例:
4 2
输出样例:
12
分析:找大佬问过之后终于懂了,正解是dp,需要画图找一下规律。
用dp[i][j]表示前i个有j个单调队列。
dp代码:
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
#define mod 123456
int dp[505][505];
int n,k;
int main()
{
cin.tie(0),ios::sync_with_stdio(false);
cin>>n>>k;
for(int i = 1;i <= n ;i++)
dp[i][1] = 2;
for(int i = 3; i <= n; i++) {
for(int j = 2; j <= i-1; j++) {
dp[i][j] =( dp[i-1][j-1] *2 + dp[i-1][j] * j +dp[i-1][j-2] *(i-j));
dp[i][j] %= mod;
}
}
cout<<dp[n][k]<<endl;
return 0;
}
暴力的代码也仍然放在这里,因为时间复杂度极大,应该只能过一点点数据,
dfs+剪枝,搜索所有的全排列情况,在搜索的过程中,统计已经放置过的数前面位置出现过的拐点数t,对于已经列出的部分,如果已经超过k-1个拐点就剪枝掉。
又因为对于后n个数,最多有n-1个拐点,如果已经排列部分有的拐点数+后面未排列的部分的最多拐点数仍少于题目要求的k-1,剪枝掉。
代码:
#include
#include
using namespace std;
int vis[505];
int a[505];
int b[505];
#define INF 0x3f3f3f3f
#define mod 123456
int n,k;
void dfs(int x,int cnt, int t) {
if( t+1 > k)
return ;
if(t+1+x-cnt < k-1)
return;
for(int i =1; i <= x; ++i){
if(vis[i] == 1)
continue;
vis[i] = 1;
a[cnt] = i;
if(cnt >= 3){
if((a[cnt-1] < a[cnt] && a[cnt-1] < a[cnt-2]) || (a[cnt-1] > a[cnt]&& a[cnt-1] > a[cnt-2]))
t++;
}
if(x == cnt) {
a[cnt] = i;
b[t+1]++;
b[t+1] %= mod;
//cout<<"t+1="<
vis[i] = 0;
return ;
}
//cout<
dfs(x,cnt+1,t);
if(cnt >= 3&&((a[cnt-1]<a[cnt]&& a[cnt-1] <a[cnt-2]) || (a[cnt-1] >a[cnt]&& a[cnt-1] > a[cnt-2])))
t--;
vis[i] = 0;
}
return ;
}
int main()
{
cin.tie(0),ios::sync_with_stdio(false);
cin>>n>>k;
if(k==1)
cout<<2<<endl;
else{
dfs(n,1,0);
cout<<b[k]<<endl;
}
return 0;
}
题目链接:https://www.acwing.com/problem/content/2557/
小明正在玩一款解谜游戏。
谜题由 24 根塑料棒组成,其中黄色塑料棒 4 根,红色 8 根,绿色 12 根 (后面用 Y 表示黄色、R 表示红色、G 表示绿色)。
初始时这些塑料棒排成三圈,如上图所示,外圈 12 根,中圈 8 根,内圈 4 根。
小明可以进行三种操作:
将三圈塑料棒都顺时针旋转一个单位。例如当前外圈从 0 点位置开始顺时针依次是 YRYGRYGRGGGG,中圈是 RGRGGRRY,内圈是 GGGR。那么顺时针旋转一次之后,外圈、中圈、内圈依次变为:GYRYGRYGRGGG、YRGRGGRR 和 RGGG。
将三圈塑料棒都逆时针旋转一个单位。例如当前外圈从 0 点位置开始顺时针依次是 YRYGRYGRGGGG,中圈是 RGRGGRRY,内圈是 GGGR。那么逆时针旋转一次之后,外圈、中圈、内圈依次变为:RYGRYGRGGGGY、GRGGRRYR 和 GGRG。
将三圈 0 点位置的塑料棒做一个轮换。具体来说:外圈 0 点塑料棒移动到内圈 0 点,内圈 0 点移动到中圈 0 点,中圈 0 点移动到外圈 0 点。例如当前外圈从 0 点位置开始顺时针依次是 YRYGRYGRGGGG,中圈是 RGRGGRRY,内圈是 GGGR。那么轮换一次之后,外圈、中圈、内圈依次变为:RRYGRYGRGGGG、GGRGGRRY 和 YGGR。
小明的目标是把所有绿色移动到外圈、所有红色移动中圈、所有黄色移动到内圈。
给定初始状态,请你判断小明是否可以达成目标?
输入格式
第一行包含一个整数 T,代表询问的组数。
每组询问包含 3 行:
第一行包含 12 个大写字母,代表外圈从 0 点位置开始顺时针每个塑料棒的颜色。
第二行包含 8 个大写字母,代表中圈从 0 点位置开始顺时针每个塑料棒的颜色。
第三行包含 4 个大写字母,代表内圈从 0 点位置开始顺时针每个塑料棒的颜色。
输出格式
对于每组询问,输出一行 YES 或者 NO,代表小明是否可以达成目标。
数据范围
1≤T≤100
输入样例:
2
GYGGGGGGGGGG
RGRRRRRR
YRYY
YGGGRRRRGGGY
YGGGRRRR
YGGG
输出样例:
YES
NO
分析:
容易看出对于每个字符串只能和对应位置i%4的值相等的地方交换位置,那么就可以分成4组,每组应该有3个G,2个R,1个Y,检查每组是否符合,用map存储一下方便处理。
代码:
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
#define mod 123456
string s1,s2,s3;
int judge(int x){
map<char,int>vis;
vis[s1[x]]++;
vis[s1[x+4]]++;
vis[s1[x+8]]++;
vis[s2[x]]++;
vis[s2[x+4]]++;
vis[s3[x]]++;
if(vis['G']== 3 &&vis['R'] == 2 &&vis['Y'] ==1)
return 1;
return 0;
}
int main()
{
int t;
cin>>t;
while(t--){
cin>>s1>>s2>>s3;
int f = 1;
for(int i = 0; i < 4 ;i++) {
if(!judge(i)) {
f = 0;
break;
}
}
if(f)
cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
题目链接:https://www.acwing.com/problem/content/2558/
在一条 R 河流域,繁衍着一个古老的名族 Z。
他们世代沿河而居,也在河边发展出了璀璨的文明。
Z 族在 R 河沿岸修建了很多建筑,最近,他们热衷攀比起来。
他们总是在比谁的建筑建得最奇特。
幸好 Z 族人对奇特的理解都差不多,他们很快给每栋建筑都打了分,这样评选谁最奇特就轻而易举了。
于是,根据分值,大家很快评出了最奇特的建筑,称为大奇迹。
后来他们又陆续评选了第二奇特、第二奇特、……、第七奇特的建筑,依次称为第二大奇迹、第三大奇迹、……、第七大奇迹。
最近,他们开始评选第八奇特的建筑,准备命名为第八大奇迹。
在评选中,他们遇到了一些问题。
首先,Z 族一直在发展,有的建筑被拆除又建了新的建筑,新建筑的奇特值和原建筑不一样,这使得评选不那么容易了。
其次,Z 族的每个人所生活的范围可能不一样,他们见过的建筑并不是所有的建筑,他们坚持他们自己所看到的第八奇特的建筑就是第八大奇迹。
Z 族首领最近很头疼这个问题,他害怕因为意见不一致导致 Z 族发生分歧。
他找到你,他想先了解一下,民众自己认为的奇迹是怎样的。
现在告诉在 R 河周边的建筑的变化情况,以及在变化过程中一些人的生活范围,请编程求出每个人认为的第八大奇迹的奇特值是多少。
输入格式
输入的第一行包含两个整数 L,N,分别表示河流的长度和要你处理的信息的数量。开始时河流沿岸没有建筑,或者说所有的奇特值为 0。
接下来 N 行,每行一条你要处理的信息。
如果信息为 C p x,表示流域中第 p 个位置 (1≤p≤L) 建立了一个建筑,其奇特值为 x。如果这个位置原来有建筑,原来的建筑会被拆除。
如果信息为 Q a b,表示有个人生活的范围是河流的第 a 到 b 个位置(包含 a 和 b,a≤b),这时你要算出这个区间的第八大奇迹的奇特值,并输出。如果找不到第八大奇迹,输出 0。
输出格式
对于每个为 Q 的信息,你需要输出一个整数,表示区间中第八大奇迹的奇特值。
数据范围
1≤L≤100000,
1≤N≤100000,
所有奇特值为不超过 109 的非负整数。
输入样例:
10 15
C 1 10
C 2 20
C 3 30
C 4 40
C 5 50
C 6 60
C 7 70
C 8 80
C 9 90
C 10 100
Q 1 2
Q 1 10
Q 1 8
C 10 1
Q 1 10
输出样例:
0
30
10
20
分析:树状数组+整体二分,这种码量考场上直接放弃吧orz
代码:
#include
#include
using namespace std;
const int N=200010;
int n,L;
struct node
{
int op,x,y,k;
int id;
}q[N],rq[N],lq[N];
int ans[N];
int tree[N];
int now[N];
int lowbit(int x)
{
return x&-x;
}
void change(int k,int x)
{
for(;k<=L;k+=lowbit(k)) tree[k]+=x;
}
int query(int k)
{
int res=0;
for(;k;k-=lowbit(k)) res+=tree[k];
return res;
}
void solve(int vl,int vr,int ql,int qr)
{
if(ql>qr) return;
if(vl==vr)
{
for(int i=ql;i<=qr;i++)
if(q[i].op==2) ans[q[i].id]=vl;
return;
}
int mid=vl+vr>>1;
int l=0,r=0;
for(int i=ql;i<=qr;i++)
{
if(q[i].op==1)
{
if(q[i].y<=mid)
lq[++l]=q[i];
else
change(q[i].x,q[i].k),rq[++r]=q[i];
}
else
{
int tmp=query(q[i].y)-query(q[i].x-1);
if(q[i].k<=tmp)
rq[++r]=q[i];
else
q[i].k-=tmp,lq[++l]=q[i];
}
}
for(int i=ql;i<=qr;i++)
if(q[i].op==1&&q[i].y>mid)
change(q[i].x,-q[i].k);
for(int i=1;i<=l;i++) q[ql+i-1]=lq[i];
for(int i=1;i<=r;i++) q[ql+l+i-1]=rq[i];
solve(vl,mid,ql,ql+l-1);
solve(mid+1,vr,ql+l,qr);
}
int main()
{
cin>>L>>n;
int cnt=0;
for(int i=1;i<=n;i++)
{
ans[i]=-1;
char op;
int x,y;
cin>>op>>x>>y;
if(op=='C')
{
if(now[x])
{
q[++cnt]={
1,x,now[x],-1};
q[++cnt]={
1,x,y,1};
now[x]=y;
}
else
{
q[++cnt]={
1,x,y,1};
now[x]=y;
}
}
else
q[++cnt]={
2,x,y,8,i};
}
solve(0,1e9,1,cnt);
for(int i=1;i<=n;i++)
if(ans[i]!=-1) cout<<ans[i]<<'\n';
return 0;
}
小 C 最近迷上了一款游戏。
现在,在游戏中,小 C 有一个英雄,生命值为 x;敌人也有一个英雄,生命值为 y。
除此以外,还有 k 个士兵,生命值分别为 a1、a2、……、ak。
现在小 C 打算使用一个叫做“燃烧权杖”的技能。
“燃烧权杖”会每次等概率随机选择一个活着的角色(英雄或士兵),扣减其 10 点生命值,然后如果该角色的生命值小于或等于 0,则该角色死亡,不会再被“燃烧权杖”选中。
“燃烧权杖”会重复做上述操作,直至任意一名英雄死亡。
小 C 想知道使用“燃烧权杖”后敌方英雄死亡(即,小 C 的英雄存活)的概率。
为了避免精度误差,你只需要输出答案模一个质数 p 的结果,具体见输出格式。
输入格式
输入包含多组数据。
输入第一行包含一个正整数 T,表示数据组数。
接下来 T 组,每组数据第一行包含四个非负整数 x、y、p、k,分别表示小 C 的英雄的生命值、敌方英雄的生命值,模数和士兵个数。
第二行包含 k 个正整数 a1、a2、……、ak,分别表示每个士兵的生命值。
输出格式
对于每组数据,输出一行一个非负整数,表示答案模质数 p 的余数。
可以证明,答案一定为有理数。设答案为 a/b(a 和 b 为互质的正整数),你输出的数为 x,则你需要保证 a 与 bx 模 p 同余;也即,x=(a⋅b−1)modp,其中 b−1 表示 b 模 p 的逆元, mod 为取模运算。
数据范围
1≤x,y,a1,…,ak≤109,
3≤p≤10000 且 p 为质数,
0≤k≤10
输入样例:
6
1 10 101 0
100 1 101 0
50 30 4903 2
1 1
987 654 233 1
321
1000000000 999999999 233 3
1 2 3
1000000000 999999999 3 3
1 2 3
输出样例:
51
37
1035
118
117
2
样例解释
对于第一组数据,所求概率即为“燃烧权杖”第一次就扣减敌方英雄 10 点生命值的概率,即 1/2。2×51 模 101 余 1。
对于第二组数据,答案为 1023/1024,1024×37 与 1023 模 101 同余。
对于第三组数据,答案为 99/128。