第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛非官方题解


7-1 切蛋糕

龙龙有一块蛋糕,现在他想将蛋糕平均切成k块,分给他的k名hxd。但是不幸的是,因为龙龙不擅长切蛋糕,他每一次只能将一块蛋糕平均分成两份。例如,将一块大小为1的蛋糕分割成两块大小为1/2的蛋糕,将一块大小为1/2的蛋糕分割成两块大小为1/4的蛋糕,以此类推。由于龙龙手法有限,蛋糕的大小最小不能小于1/2​15​​。

除此之外,为了更有仪式感,龙龙在将切好的蛋糕分给自己的hxd之前,要先将蛋糕打包。龙龙可以将切好的任意数量块蛋糕打包在一起,并交给他的一位hxd。需要注意的是,蛋糕在打包好后就不能再被切分了。为了公平起见,龙龙希望他的每一位hxd分得的蛋糕大小是相等的,即每位hxd分得的蛋糕的大小,与1/K的差距的绝对值不能大于1/2​10​​。因为龙龙很懒,所以他希望蛋糕的总操作次数(每次打包或切分算一次操作)不超过6000。

现在龙龙想请你为他设计一种方案,使得他能够在6000次操作内,将蛋糕公平地分给他的hxd们。 输入格式:

输入在一行中给出1个不超过2​10​​的正整数K。 输出格式:

在第一行输出一个不超过6000的正整数N,表示方案的操作次数。

接下来的N行描述该方案的N次操作。

对于第i次操作,若该操作要进行切分操作,则在该行行首输出1,之后输出要切分的蛋糕的大小。例如:若要将一块大小为1/2​1​​的蛋糕分割成两块大小为1/2​2​​的蛋糕,则在该行输出1
1;若要将一块大小为1/2​2​​的蛋糕分割成两块大小为1/2​3​​的蛋糕,则在该行输出1 2。初始时完整的蛋糕,大小视为1/2​0​​。

若该操作要进行打包操作,则在该行行首输出2,之后输出一个正整数m,代表要打包的蛋糕块数。之后输出m个正整数,分别代表要打包的m快蛋糕的大小。例如:若要将一块大小为1/2​1​​的蛋糕和一块大小为1/2​2​​的蛋糕进行打包,则在该行输出2
2 1 2;若要将两块大小为1/2​2​​的蛋糕进行打包,则在该行输出2 2 2 2。

需要注意,一块蛋糕只有在被打包后才可以被龙龙送给他的hxd。所以你至少应进行K次打包操作。每份被包装的蛋糕的大小与1/K的差距的绝对值不应大于1/2​10​​。
输入样例:

在这里给出一组输入。例如:

2

输出样例:

在这里给出相应的输出。例如:

3 1 0 2 1 1 2 1 1

代码:

//这是我们军军的代码 orz
#include<bits/stdc++.h>
#define int long long
using namespace std;

signed main() 
{
	int k;
	cin >> k;
	
	cout << k + (1 << 11) - 1 << endl;
	
	for(int i = 0; i < 11; i ++) 
	{
		for(int j = 1; j <= (1 << i); j ++) 
		{
			printf("1 %d\n", i);
		}
	}
	
	int cnt = (1 << 11) / k;
	
	for(int i = 1; i <= k; i ++) 
	{
		printf("2 %d ", cnt);
		for(int j = 1; j <= cnt; j ++)
		{
			printf("11 ");
		} 
		printf("\n");
	}
	
	return 0;
}

7-2 小宝的幸运数组

对于小宝来说,如果一个数组的总和能够整除他的幸运数字k,就是他的幸运数组,而其他数组小宝都很讨厌。现在有一个长度为n的数组,小宝想知道这个数组的子数组中,最长的幸运子数组有多长。

对于子数组的定义,如果可以通过从开头和从结束分别删除若干个(可以为零或全部,前后删除个数不必相同)元素来从数组b获得数组a,则称数组a是数组b的子数组。(子数组包含原数组,但不包含空串)
输入格式:

多组输入。第一行包含一个整数T(1≤T≤10),表示有T组测试数据。

每组测试数据包含两行,第一行包含两个整数n和k(1≤n≤10​5​​,1≤k≤10​5​​),分别表示数组长度和小宝的幸运数字。第二行包含n个空格分隔的整数a​1​​,a​2​​,…,a​n​​(0≤a​i​​≤10​9​​),为数组的元素。
输出格式:

对于每组数据,输出和能被k整除的最长子数组的长度。如果没有这样的子数组,则输出−1。每组数据输出一行。 输入样例:

在这里给出一组输入。例如:

4 3 3 1 2 3 3 5 1 2 3 3 7 1 2 3 1 6 5

输出样例:

在这里给出相应的输出。例如:
3
2
-1
-1

思路

使用前缀和sum[]预处理数组,则从第i位到第j位的子串和为sum[j]-sum[i-1],又因为本题寻找的是最长子数组,所以需要从大到小枚举子树组,找到了就输出然后跳出循环,这样可以防止运行超时。

代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int t;
    scanf("%d",&t);//这题时间卡的紧,用scanf比较好
    while(t--)
    {
        long long n[100005]={0},sum[100005]={0};//每组数据都要重新归零
        int a,b;
        scanf("%d %d",&a,&b);
        for(int i=1;i<=a;i++)
        {
            scanf("%d",&n[i]);
            sum[i]=sum[i-1]+n[i];//前缀和预处理
        }
        int k=a,flag=0;
        while(k!=0&&flag==0)
        {
            for(int i=a;i-k>=0;--i)//从最长的子数组开始枚举
            {
                if((sum[i]-sum[i-k])%b==0)//前缀和相减得到子数组的和
                {
                    printf("%d\n",k);
                    flag=1;//找到了一项
                    break;
                }
            }
            --k;//子数组长度减1再次循环
        }
        if(flag==0)//一项也没有
        printf("-1\n");
    }
    return 0;
}

这是军军运用STL的一种巧妙的算法
vector妙啊 妙啊

#include <bits/stdc++.h>
using namespace std;

int main() 
{
	int t;
	cin >> t;
	while (t --) 
	{
		int n, k;
		cin >> n >> k;
		
		vector <int> v (k, -1);
		v[0] = 0;
		
		int ans = -1, sum = 0;
		
		for(int i = 1; i <= n; i ++) 
		{
			int temp;
			cin >> temp;
			
			sum += temp;
			sum %= k;
			
			if (v[sum] != -1) ans = max (ans, i - v[sum]);
			else v[sum] = i;
		}
		cout << ans << endl;
	}
	return 0;
}

7-3 上进的凡凡

凡凡是一个上进的人,他的人生没有下坡路,他也讨厌带有”下坡路“的东西。

所以,对于凡凡来说,只有非降序的数组才是nice的(如:1,2,2,3,4,5,5);若数组元素个数为1,也满足非降序,也是nice的。

现在有一个长度为n的数组,凡凡想知道它的子数组中有多少个数组是nice的。

你能帮帮他吗?

对于子数组的定义,如果可以通过从开头和从结束分别删除若干个(可以为零或全部,前后删除个数不必相同)元素来从数组b获得数组a,则称数组a是数组b的子数组。(子数组包含原数组,但不包含空串)
输入格式:

输入共两行。

第一行输入一个整数n(1≤n≤10​5​​),表示数组的长度。

第二行包含n个空格分隔的整数a​1​​,a​2​​,…,a​n​​(0≤a​i​​≤10​9​​),为数组的元素。 输出格式:

输出给定数组的子数组中是nice数组的个数。(注意使用long long)
输入样例:

在这里给出一组输入。例如:

5 1 2 3 4 5

输出样例:

在这里给出相应的输出。例如:

15

思路:又是求子数组的一题,这题应该是最简单的一题了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1000005;
ll a[maxn];
ll dp[maxn];
ll ans=0;
int main()
{
	std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);//这一行是为了用cin、cout也能很快输入输出 看不懂可以直接忽略
	ll n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	 }
	 ll sum=1;
	 for(int i=1;i<n;i++)
	 {
	 	if(a[i+1]>=a[i])//大于等于
	 	{
	 		sum++;
		 }
		 else //如果碰到下一个不递增了,那就先计算出这一个递增的子数组中一共有多少个nice数组
		 {
		 	ans+=(1+sum)*sum/2;//计算nice数组的共识 比如1 2 3 4 5中有5+4+3+2+1个nice数组,所以可以用公式(1+5)*5/2来计算 
		 	sum=1;//这一步再把sum归1开始寻找接下来的子数组 因为就算子数组只有一个 它本身也是nice数组 所以是归1而不是归0
		 }
	 }
	 ans=(1+sum)*sum/2+ans; 
	 cout<<ans<<endl;
}

7-4 Seek the Joker I

长达数日的春日祭终于告一段落,作为巫女的朝野芳乃在打扫完神社本决定好好享受一下久违的宁静。然而守护了神刀数百年的丛雨难耐寂寞,希望芳乃能陪她一起玩扑克消解愁闷。

芳乃并不擅长市井的游戏,所以总是输多赢少。而昨日被芳乃的神乐舞深深吸引,以致一早就前来建实神社希望能再睹芳华的你碰巧听见了此事。尽管不知道为什么美丽的巫女要自言自语地为玩扑克而苦恼,但你毅然决然地毛遂自荐,希望能为芳乃一解眉间愁。

芳乃告诉了你丛雨准备了n张扑克牌作为牌堆,每人每次至多从牌堆顶部抽k张牌,至少抽1张牌。牌堆底部的最后一张牌作为乌龟,抽中的将输掉这一轮比赛。芳乃想知道在你的帮助下,她和丛雨都采取积极策略时,她自己是否一定能获胜。作为被丛雨邀请的一方,每轮游戏都是芳乃先抽。

因为看不见丛雨而误认芳乃罹患精神分裂的你在不由感叹红颜薄命的同时,决定尽全力帮助芳乃完成她的委托。

声明:本题中的所有角色在剧情发生时均已超过18岁。
输入格式:

第一行包含一个整数T,表示共有T组测试数据。

每组测试数据共一行,包含两个正整数n和k,分别表示牌堆中有n张牌和每次抽取最多抽取k张。

数据保证,T,n,k≤10​6​​。
输出格式:

对于每组测试数据给出一行结果。

如果芳乃必胜,则输出yo xi no forever!,

否则输出ma la se mi no.1!。
输入样例:

4 1 1 23 2 6 4 114 514

输出样例:

ma la se mi no.1!
yo xi no forever!
ma la se mi no.1!
yo xi no forever!

思路:博弈论中的巴什博弈,我的另外一篇博客中正好是讲的这个
巴什博弈
可以直接记一下结论:

巴什博弈(Bash Game)

只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。

最后取光者胜

若n%(m+1)==0,后手必胜,反之,先手必胜。

最后取光着输

当(n-1)%(m+1)==0 时 ,后手必胜,否则先手必胜

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int n,k;
		cin>>n>>k;
		if(n==1)
		{
			cout<<"ma la se mi no.1!"<<endl;
		}
		else if(k==1)
		{
			if(n%2==1)cout<<"ma la se mi no.1!"<<endl;
			else cout<<"yo xi no forever!"<<endl;
		}
		else if(k>=n)
		{
			cout<<"yo xi no forever!"<<endl;
		}
		else //除去前面几个特殊的情况
		{
		   	if((n-1)%(k+1)==0)cout<<"ma la se mi no.1!"<<endl;//直接背结论
		   	else cout<<"yo xi no forever!"<<endl;
		}
	}
}

7-5 Seek the Joker II

长达数日的春日祭终于告一段落,作为巫女的朝野芳乃在打扫完神社本决定好好享受一下久违的宁静。然而守护了神刀数百年的丛雨难耐寂寞,希望芳乃能陪她一起玩扑克消解愁闷。

芳乃并不擅长市井的游戏,所以总是输多赢少。而昨日被芳乃的神乐舞深深吸引,以致一早就前来建实神社希望能再睹芳华的你碰巧听见了此事。尽管不知道为什么美丽的巫女要自言自语地为玩扑克而苦恼,但你毅然决然地毛遂自荐,希望能为芳乃一解眉间愁。

芳乃告诉了你丛雨准备了n张扑克牌作为牌堆,自牌顶向下数第x张牌作为乌龟,即“乌龟”的上方有x-1张牌,“乌龟”的下方有n-x张牌,抽中“乌龟”的将输掉这一轮比赛。每人每次可以同时在牌堆顶和牌堆底或者仅在牌堆顶或牌堆底其抽取任意张牌,至少抽1张牌。但若选择同时在牌堆顶和牌堆底抽牌,则抽牌数量需要相同。芳乃想知道在你的帮助下,她和丛雨都采取积极策略时,她自己是否一定能获胜。作为被丛雨邀请的一方,每轮游戏都是芳乃先抽。

因为看不见丛雨而误认芳乃罹患精神分裂的你在不由感叹红颜薄命的同时,决定尽全力帮助芳乃完成她的委托。

声明:本题中的所有角色在剧情发生时均已超过18岁。
输入格式:

第一行包含一个整数T,表示共有T组测试数据。

每组测试数据共一行,包含两个正整数n和x,分别表示牌堆中有n张牌和”乌龟“的位置。

数据保证,T≤10​6​​ 和 n,x≤3∗10​6​​。
输出格式:

对于每组测试数据给出一行结果。

如果芳乃必胜,则输出yo xi no forever!,

否则输出ma la se mi no.1!。
输入样例:

8 1 1 10 3 17 6 12 5 4 3 9 6 12 8 17 11

输出样例:

ma la se mi no.1! yo xi no forever! yo xi no forever! ma la se mi
no.1! ma la se mi no.1! ma la se mi no.1! ma la se mi no.1! ma la se
mi no.1!

思路:
又是一道关于博弈论的题目,这次是威佐夫博弈。

威佐夫博弈
有两堆各若干的物品,两人轮流从其中一堆取至少一件物品,至多不限,或从两堆中同时取相同件物品,规定最后取完者胜利。

若两堆物品的初始值为(x,y),且x<y,则另z=y-x;

记w=(int)[((sqrt(5)+1)/2)*z ];(这个正好是黄金分割率)数学就是这么的奇妙

若w=x,则先手必败,否则先手必胜。

大家就把结论记着就行了,反正我是缕不明白

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int main(){
    double mysticalConstant = (1.0+sqrt(5.0))/2.0;
    int T;
    cin>>T;
    while(T--){
        int n,x;
        cin>>n>>x;
        int a=x-1,b=n-x;
        if(a>b) swap(a,b);
        int temp = (b-a)*mysticalConstant;
        if(temp!=a) cout<<"yo xi no forever!\n";
        else
            cout<<"ma la se mi no.1!\n";
    }
    return 0;
}

7-6 成绩查询ING

去年的新冠疫情爆发让众多大学生只能只能在家里上学,老师为了方便自己录入成绩和方便大家成绩查询,建立了一个录入和查询成绩的系统,能完成M次两种不同的查询,输入查询次数M,查询M次,每次首先输入查询的模式T,T为1时,输入同学的姓名Name,并依次输出同学的成绩Grade(0<=Grade<=100),
学号(0~1000000},性别(1/2),T为2时,输入成绩,输出有具体有哪些同学考到了这个分数,输出同学的Name,并要求按字典序输出,当没有同学为此分数时,则不输出。
字典序,对于字符串,先按首字符排序,如果首字符相同,再按第二个字符排序,以此类推。 输入格式:

第一行包含一个整数N,表示系统中共有N个人(1<=N<=100000)。
下面N行分别输入N个人的姓名Name,成绩Grade(成绩在0~100之间),性别(1或2分别表示男性、女性),学号。表示系统中成员的信息
输入查询次数M(M<=10000000),接下来M行完成M次查询任务 输出格式:

输出M次查询的结果,当T为1时,输入同学的姓名Name,并在一行中依次输出同学的成绩Grade(0<=Grade<=100),
学号(0~1000000},性别(1/2),用空格间隔(注意行末无空格),T为2时,输入成绩,输出有具体有哪些同学考到了这个分数,输出同学的Name(每个Name输出一行,无空格),并要求按字典序输出,当没有同学为此分数时,则不输出。
输入样例:

5 N 28 2 7475 UN 83 2 27550 EXF 5 2 17298 OVYNH 51 2 14827 XNV 53 1
7591 2 1 XNV 2 27

输出样例:

53 7591 1

思路:

学长之前再三提醒,要好好学STL,自己也确实去努力学了学,但是图灵杯的时候确实没时间写555
看了看官方题解,主要运用到了map和set

map我之前有做过笔记,挺好用的
map的笔记

//这是官方题解的代码
#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;
    cin>>n;
    set<string> s[120];
    map<string,int> mp1;
    map<string,int> mp2;
    map<string,int> mp3;
    for(int i=1;i<=n;i++)
    {
        string Name;
        int xb;
        int xh,cj;
        cin>>Name>>cj>>xb>>xh;
        s[cj].insert(Name);
        mp1[Name]=cj;
        mp2[Name]=xh;
        mp3[Name]=xb;
    }
    int m;
    cin>>m;
    while(m--){
        int x;
        cin>>x;
        if(x==1){
            string name;
            cin>>name;
            cout<<mp1[name]<<" "<<mp2[name]<<" "<<mp3[name]<<endl;
        }else{
            int y;
            cin>>y;
            set<string>::iterator it;//这一步不懂的朋友可以去查一下iterator的用法
            it=s[y].begin();
            for(;it!=s[y].end();it++){
                cout<<*it<<"\n";
            }
        }
    }
    return 0;
}

7-7 贪吃的派蒙

在遥远的提瓦特大陆上,正在筹备一年一度的羽球节,猎鹿人餐厅为犒劳认真筹备的众人,准备了K份甜甜花酿鸡供大家排队领取。

在每一次的排队中,编号为i的角色领取上限为Ai,这意味着他可以领取的甜甜花酿鸡在[1-Ai]范围内。当一个角色领完本次的甜甜花酿鸡,他/她就会回到队列的末尾,直到所有甜甜花酿鸡都被吃完为止。当轮到一个角色领取时,如果所有的甜甜花酿鸡都被领完,那么他/她就要帮大家刷盘子。

贪吃的派蒙每次都吃固定的Ax个甜甜花酿鸡(如果剩下的甜甜花酿鸡的数量比Ax小,那么他就把剩下的都吃完)。我们很容易找到派蒙的编号,Ax比其他所有的Ai都要大。大家都想让派蒙最后留下来刷盘子,请你写一个程序来判断这是否可能。
输入格式:

多组输入。

第一行包含一个整数T(1≤T≤100),表示有T组测试数据。

接下来每组测试数据包含两行。

第一行包含两个整数N和K(2≤N≤105,0≤K≤108),分别表示人数和甜甜花酿鸡的数量。

第二行包含一个整数Ai(1≤Ai≤10^9),表示队列中编号为i的角色可以领取甜甜花酿鸡的最大数量。始终只有一个最大的Ax。 输出格式:

如果大家能找到一种方案让派蒙刷盘子,那么输出“YES”。否则输出“NO”。
输入样例1:

1 4 3 1 2 3 2

输出样例1:

YES

输入样例2:

1 5 8 1 2 3 2 1

输出样例2:

NO

思路:

最讨厌这种题目了,我也知道用贪心写,可是真的好难想,每次见到这种题目第一反应就是:啊?这也能编程?

看了官网题解还是没整明白 气死我了 看了隔壁的大佬写的,感觉点意思了,把隔壁大佬的代码抄过来给大家看看吧 嘻嘻

#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=1e5+7;
const int mod=998244353;

ll num[maxn];

int main()
{
    int t;scanf("%d",&t);
    while(t--)
    {
        int n;
        ll k;
        scanf("%d%lld",&n,&k);
        ll M=0;
        int tar=0;//记录下派蒙在第几个
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&num[i]);
            if(num[i]>M)
            {
                tar=i;
                M=num[i];
            }
            num[i]+=num[i-1];//前缀和
        }
        ll yici=M+n-1;//代表一次循环最少要吃掉多少个
        if(k<tar-1) printf("NO\n");//前面的tar个人最少每个人要吃一个
        else
        {
            ll rest=(k-tar+1)%(yici);//代表除了派蒙的人每人都只吃1个的情况,最后剩下的部分
            ll cas=(k-tar+1)/yici;//在上述情况下总共有多少轮循环(这里的循环,派蒙是第一个人)
            cas*=(num[n]-M);//每轮循环我们最多还可以再多吃总的个数减去派蒙的个数
            cas+=num[tar-1]-tar+1;//在开始循环前,一开始就排在派蒙前面的tar-1个人,除了最基本的每人吃一个外,最多还能吃几个
            if(cas>=rest) printf("YES\n");//如果能多吃的部分大于剩余的部分,代表我们能有一种安排,使得某次循环开始时,派蒙去吃东西的时候k已经为0
            else printf("NO\n");
        }
    }
}

7-8 数羊

憨憨小杨晚上睡不着觉,就开始数羊,她觉得一只一只数太慢了,突发奇想出了一种新的数羊方式,羊羊数量A(n,m)由两个整形变量n和m决定,计算方式如下:

图片2.png

现在给出n和m的值,请你帮小杨数数一共有多少只羊。 输入格式:

多组输入。

第一行包含一个整数T(1≤T≤1000),表示有T组测试数据。

每组测试数据包含一行,包含两个整数n(1≤n≤10^9)和m(0≤m≤2). 输出格式:

对每一组输入,在一行中输出A(n,m)的值,由于输出的结果可能会很大,答案对998244353取模。 输入样例:

3 3 0 3 1 3 2

输出样例:

5 6 8

思路:
以后看到这种看似没有规律的题,硬写肯定会超时,所以要去打表找规律,这题的规律就十分简单了。
以下是从官方题解里抄过来的嘻嘻:

暴力打表可知:
m=0时,A(n,0)=n+2;
m=1时,A(n,1)=2*n
m=2时,A(n,2)=2^n
注意取模

它官方题解里的证明反正我是看不懂,也不想看

这里还用的了一个快速幂的知识 不会的可以去洛谷做一下模版题
快速幂

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
ll quick_mod(ll a,ll m)//快速幂 
{
    ll ans=1;
    while(m!=0)
    {
        if(m%2==1) ans=ans*a%mod;
        a=a*a%mod;
        m/=2;
    }
    return ans%mod;
}
int main()
{
    cin.tie(0); ios::sync_with_stdio(false);
    int t; ll n,m;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        ll ans;
        if(m==0)
        {
            ans=n+2;
        }
        else if(m==1)
        {
            ans=n*2;
        }
        else if(m==2)
        {
            ans=quick_mod(2,n);//调用快速幂函数
        }
        cout<<ans%mod<<endl;
    }
    return 0;
}

7-9 买花

情人节马上要到了,阳阳想送出n朵花给喜欢的妹妹,他打算提前开始买。但是,因为他有强迫症,所有的花要分k天买(k>1,即不能一天全买完),第一天他可以买任意朵花,之后每一天买花的数量为前一天的两倍,(如若第一天买4朵,第二天就要买8朵,以此类推)。

现在离情人节还有15天(k≤15),请你告诉阳阳,他能不能刚好买到n朵花。
输入格式:

多组输入。第一行一个正整数T(1≤T≤10​5​​),表示数据组数。

接下来T行,每行一个正整数n(1≤n≤10​9​​),表示预计买花的数量。

输出格式:

每组数据输出一行,共T行。

判断能否刚好买到n朵花,可以则输出"YE5",否则输出"N0"。
输入样例:

2 21 20

输出样例:

YE5 N0

思路:
很简单的一题,就是这个YE5和N0也太tmd坑了,气死我了!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1000005;
ll a[maxn];
ll sum[maxn];
int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	ll t;
	cin>>t;
	while(t--)
	{
		ll n;
		cin>>n;
		ll i=1; 
		ll sum=1;
		ll cnt=1;
		while(1)
		{
			cnt++;
			i=i*2;
			sum+=i;
			if(n%sum==0)
			{
				cout<<"YE5"<<endl;
				break;
			}
			if(cnt==15)
			{
				cout<<"N0"<<endl;
				break;
			}
			
		}
	}
}

7-10 这是一道简单的模拟

财务计划要从家里出发,去N个城市出差,然后再回到家中,但N个出差地点之间不一定都能通车,现在他要筛选出花费最少的路径,你能帮帮他吗?
输入格式:

第一行为两个正整数N和M(1≤N≤300,1≤M≤​2​​N(N+1)​​),分别表示有N个出差地点和这些地点之间的M条通路,其中出差地点用1到N编号,而财务的家所在城市用编号0表示。

随后的M行,每行给出通路连接的两个城市和这条通路上的花费,格式为:

城市A 城市B 花费

通路是双向的,且两个城市间最多有一条通路,不存在自环。保证所有花费大于0。

再下一行给出一个正整数K,表示现在有K条推荐路径(注意:给出的路径不一定能通过或可能不满足财务的要求)。

接下来K行每一行表示一个推荐路径,第一个整数n表示途径的城市数,后面有n个整数x​i​​(1≤x​i​​≤N)表示途经的城市(不包括财务的家),如:

3 1 2 3

表示实际路径为0→1→2→3→0。 输出格式:

请你检验给出的K条推荐路径,当它满足:

1.给出的路径能实际通车,即路径中相邻城市存在通路;
2.给出的路径恰好能都到达N个出差城市一次,即不能漏掉某个城市,也不能重复到达。

则称这条路径是可行的。

对于给出的K条推荐路径,请输出其中可行路径中最少的花费,若不存在可行路径,请输出"-1"。(题目保证花费和不超过int范围) 输入样例:

在这里给出一组输入。例如:

5 10 0 1 5 0 5 12 1 2 2 2 3 8 3 4 13 1 3 11 0 2 5 0 4 9 4 5 6 3 5 7 5
5 1 3 2 3 1 5 3 2 1 4 5 5 2 1 3 5 4 6 1 2 3 4 5 1 5 1 2 3 5 4

输出样例:

在这里给出相应的输出。例如:

37

思路:

就是一道简单的模拟题,有几个小点要注意:
1、通路是双向的。
2、恰好经过每个城市一次。
这题充分暴露了自己写代码其实不够熟练,一次性的正确率太低,debug了好久还好被自己找出错误来了,但是浪费了大量的时间。

#include<bits/stdc++.h>
using namespace std;
int dp[305][305];//二维数组记录两个城市通路的费用
int vis[305];//vis来标记有没有经过这个城市
int f[305];
const int maxn=1000000000;//记录最大值 下面好比较
int main()
{
	int n,m;//n个出差地点 m条路
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int a,b,cost;//城市A 城市B 花费
		cin>>a>>b>>cost;
		dp[a][b]=cost;
		dp[b][a]=cost;//注意通路是双向的 都要记录一下
	 } 
	 int k;//k条路径
	 cin>>k;
	 int res=maxn;
	 for(int i=1;i<=k;i++)
	 {
	 	int num;
	 	cin>>num;//途径的城市数量 
	 	int flag=1;
	 	int sum=0; 
	 	memset(vis,0,sizeof(vis));//每组路径都记得要归零 不然会有重复
	 	for(int j=1;j<=num;j++)
	 	{
		 cin>>f[j];
		 vis[f[j]]++;//表示这个城市已经访问过了
		 }
		 for(int z=1;z<=n;z++) 
		 {
		 	if(vis[z]==0||vis[z]>1)//访问过了或者没有访问到都不行
		 	{
		 		flag=0;
		 		break;
			 }
		 }
		 if(flag==0)
		 {
		 	sum=maxn;//sum的值归位最大值表示路径不存在
		  } 
		  else{
		  for(int z=1;z<=num;z++)
		  { 
			 	if(dp[f[z-1]][f[z]]!=0)sum+=dp[f[z-1]][f[z]];
			 	else 
			 	{
			 		sum=maxn;
			 		break;
				 }
		  } 
		}
		if(sum!=maxn&&dp[f[num]][0]!=0)//最后还要回到家不要漏了
		{
		sum+=dp[f[num]][0];//加上回到家的钱
		if(sum<res)res=sum;//完整的路径的需要的话费
		}
	 }
	 if(res==maxn)cout<<"-1";//不存在此条路径
	 else cout<<res;
}

7-11 黑洞密码

近些日子,某科学家接受到了来自外太空的神秘讯息,在经过了一段时间的研究后,科学家发现讯息是一个由字母和数字组成的字符串str,想要破译,需要通过一定的规则将字符串进行转换。规则如下:

1.确定讯息的长度为32;

2.字符串中第4n+1∼4n+4的字母和第4n+1∼4n+4(0≤n≤3)的数字为一组,共4组;

3.每组的第1,2,3,4个字符分别往后推每组第1,2,3,4个数字个数 例:如第一个字母为a,第一个数字为3,转换后变为d,'z'之后是'B','Z'之后是'b';

4.将每组内部字母的顺序颠倒;

5.将四组字符合并就是最后的讯息。

输入格式:

输入一个长度为32的字符串 输出格式:

输出转换后的密码 输入样例:

在这里给出一组输入。例如:

Zzc6Ltw2OD4yR640263W7G8G30HW9C71

输出样例:

在这里给出相应的输出。例如:

RgCgJQwxJfYCDeQG

思路:
这题其实也是一道模拟题,但是也有点小坑,一开始要仔细研究样例的找出变化的规律
16个字母:ZzcL twOD yRWG GHWC
16个数组:6246 4026 3783 0971
然后按照题目的规律,这里要注意:‘z’之后是’B’,‘Z’之后是’b’,一开始没看到这句话,还以为题目出错了,害,真实太难了
然后顺序颠倒前:gCcR xwQJ CYfJ GQeQ
发现了规律事把每组都点到颠倒了一下最终得到了答案
RgCg JQwx JfYC DeQG

代码:

#include<iostream>
using namespace std;
int cnt1, cnt2;
char xx[36] = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','B','C','D','E','F','G','H','I','J' };//我是用的打表的笨办法,因为最多往后右移9个,也不是很难做到
char dx[36] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','b','c','d','e','f','g','h','i','j' };//区分小写 大写就可以了
int main()
{
    string s;
    cin >> s;
    char a[16];//记录下字母
    int  b[16];记录下数字
    cnt1=0;//数字个数
    cnt2=0;//字母个数
    for (int i = 0; i < 32; i++)
    {
        if (s[i] >= '0' && s[i] <= '9')
        {
            b[cnt1] = s[i]-'0';//因为开的是int型的数组,记得减去‘0’得到数字
            cnt1++;//数字个数++
        }
        else if ((s[i] >= 'a' && s[i] <= 'z') || (s[i] >= 'A' && s[i] <= 'Z'))
        {
            a[cnt2] = s[i];
            cnt2++;//字母个数++
        }
    }
    
    for (int i = 0; i < 16; i++)
    {
        if(a[i]>='a'&&a[i]<='z')
        {
            for(int j=0;j<35;j++)//找到表中对应的位置
            {
                if(a[i]==xx[j])
                {
                a[i]=xx[j+b[i]];//加上需要向后移动的位置就是变换成的字母了
                break;
                }
                
            }
        }
        else if(a[i]>='A'&&a[i]<='Z')//同理
        {
            for(int j=0;j<35;j++)
            {
                if(a[i]==dx[j])
                {
                a[i]=dx[j+b[i]];
                break;
                }
            }
        }
    }
    int count=4;
    while(1)
    {
        if(count>32)break;//一共输出16个 
        for(int i=count-1;i>=count-4;i--)//每四个颠倒一次顺序后再输出
        {
            cout<<a[i];
        }
        count+=4;
    }
}

7-12 建立火车站

新冠疫情,导致了各个城市之间物资输送的障碍。假设有N个城市在一条直线上,为了物资能顺利抵达各个城市,可以在路线上建立最多个数为K个暂时停靠站,由于火车在两个站台(城市也算站台)之间的距离越近,需要的总花费越少,因此我们需要让火车相邻两个站台之间的最大距离最小,求出距离L,2
≤N ≤100000, 0 ≤K ≤100000,所有城市坐标小于等于10^12,且不存在负值。提醒:
城市坐标均为正整数,且停靠站只能建在整数坐标点上。
输入格式:

第一行输入城市个数N,可建立停靠站个数K, 第二行输入N个城市的坐标(不保证前一个城市坐标比后一个城市小)。

输出格式:

输出L

输入样例:

2 2 4 106

输出样例:

34

思路:
是一道很好的二分答案的题目,我对二分答案的理解就是使用二分法更快的枚举出答案。
比如说这题,他是先找出一个mid,“假装”他就是本题的解了,然后再一一和两个车站间的距离进行比较,如果这两个车站的距离比mid要大,那就要塞一个临时车站进去,如果两个车站距离小于等于mid,那就是符合条件的,继续往后看就行了,最后,看这种情况下需要塞几个临时车站进去,将这个数量和题目给的最大能塞的车站数进行比较。
对于我们最后站台之间相邻距离的最大值L,随着L的增大,我们需要设置的站台数量只可能变少不可能变多。满足二分条件。存在某一个值x,使得当L>=x时,站台数量<=k。
可以借此写出一个二分。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+7;
ll num[maxn];
ll dis[maxn];//记录两个相邻车站间的距离
int n,k;//n个城市 k个暂时停靠站
bool check(ll x)//判断mid
{
    ll sum=0;
    for(int i=1;i<n;i++)
    {
        if(dis[i])
        {
            ll temp=dis[i]/x;
            if(dis[i]%x) temp++;//可能正好等于mid的情况 所以要特别判断一下
            sum+=temp-1;
        }
    }
    if(sum<=k)return 1;
    else return 0;
}

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
    {
        scanf("%lld",&num[i]);
    }
    sort(num,num+n);//按顺序排好
    bool flag=1;//可能会有车站重合的情况
    for(int i=1;i<n;i++)
    {
        dis[i]=num[i]-num[i-1];
        if(dis[i]!=0) flag=0;
    }
    if(flag) printf("0\n");//车站重合在了一个点上
    else
    {
        ll l=1,r=1e12;//由所有城市坐标小于等于10^12,所以令r=1e12
        while(l<r)//二分法开始!
        {
            ll mid=(l+r)/2;//选中一个mid小朋友假装它是答案
            if(check(mid)==1) r=mid;//mid选的大了 临时停靠站没有用满 所以要缩小mid再进行比较
            else l=mid+1;//mid选的小了 需要更多的临时停靠站 所以要增大mid进行比较
        }
        printf("%lld\n",l);
    }
}
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值