GLPT团队程序设计天梯赛 模拟赛 2023

L1-1 嫑废话上代码

#include
using namespace std;
int main(){
	cout<<"Talk is cheap. Show me the code.";
	return 0;
}

L1-2 九牛一毛

#include
using namespace std;
int main(){
	int n;cin>>n;
	cout<<n/15<<" "<<n/20<<" "<<n*90;
	
	return 0;
}

L1-3 小孩子才做选择,大人全都要

阿汪面前有两只盲盒,每只盒子打开都有两种可能:或者装了 X 克狗粮,或者是一只容量为 Y
克的狗粮储蓄盒。如果是狗粮,阿汪可以快乐地吃掉;如果是空储蓄盒,那就倒霉了,阿汪必须想办法找到狗粮把这只储蓄盒装满,自己还吃不到。

正当阿汪发愁不知道该怎么选的时候,铲屎官大手一挥:“小孩子才做选择,大人全都要!”但全都要的结果,却不一定是赚了还是亏了……

我们假设聪明的阿汪总是能嗅出狗粮最多的盒子,并且绝不会选任何储蓄盒。而铲屎官没有这样的鼻子,他一定是全都要。铲屎官如果打开了有储蓄盒的盒子,就必须想办法把储蓄盒装满,他会优先用另一只盒子里的狗粮装(如果另外一只盒子里有狗粮),不够了还得自己去买新的狗粮,这样阿汪可就亏啦,什么都吃不到了。本题就请你判断阿汪到底是赚了还是亏了。

输入格式:

输入在一行中给出两个整数,绝对值都不超过
100,中间用一个空格分开,分别代表两只盒子里的东西。如果是正数就表示是狗粮的份量,如果是负数就表示绝对值是空盆的容量。两个数都肯定不是
0,因为保证没有空盒子。

输出格式:

第一行输出两个结果:如果让阿汪选能吃到的狗粮 A,和如果铲屎官全都要能吃到的狗粮
B。两个数字间用一个空格分开。如果铲屎官的决定让阿汪赚到了,就在第二行输出一个笑脸 _,否则输出一个哭脸
T_T。但如果反正什么都吃不到(两个盒子里都没有狗粮),就输出一张躺平脸 -_-。

输入样例 1:

12 18

输出样例 1:

18 30
^_^

输入样例 2:

12 -18

输出样例 2:

12 0
T_T
#include
using namespace std;
#define int long long
#define fer(i,a,b) for(int i=a;i<b;i++)
#define cf int T;cin>>T;while(T--)

signed main(){
	
	
	int a,b;cin>>a>>b;
	int dog=0,master=0;
	if(a<b)swap(a,b);
	if(a>0&&b>0){
		dog=a;
		master=a+b;
	}else if(a>0&&b<0){
		dog=a;
		if(-b>=a)master=0;
		else master=a+b;
	}else {
		dog=0;
		master=0;
	}
	cout<<dog<<" "<<master<<endl;
	if(dog!=0&&dog<=master)cout<<"^_^"<<endl;
	else if(dog!=0&&dog>master)cout<<"T_T"<<endl;
	else if(dog==0)cout<<"-_-"<<endl;
	return 0;
}

L1-4 拯救外星人 10 7980/11025(72.38%)

#include
using namespace std;
#define int long long
#define fer(i,a,b) for(int i=a;i<b;i++)
signed main(){
	int a,b;cin>>a>>b;
	int sum=1;
	fer(i,2,a+b+1){
		sum*=i;
	}
	cout<<sum;
	return 0;
}

L1-5 试试手气 15 6481/15213(42.60%)

我们知道一个骰子有 6 个面,分别刻了 1 到 6 个点。下面给你 6 个骰子的初始状态,即它们朝上一面的点数,让你一把抓起摇出另一套结果。假设你摇骰子的手段特别精妙,每次摇出的结果都满足以下两个条件:

1、每个骰子摇出的点数都跟它之前任何一次出现的点数不同;
2、在满足条件 1 的前提下,每次都能让每个骰子得到可能得到的最大点数。
那么你应该可以预知自己第 n 次(1≤n≤5)摇出的结果。

输入格式:
输入第一行给出 6 个骰子的初始点数,即 [1,6] 之间的整数,数字间以空格分隔;第二行给出摇的次数 n(1≤n≤5)。

输出格式:
在一行中顺序列出第 n 次摇出的每个骰子的点数。数字间必须以 1 个空格分隔,行首位不得有多余空格。

输入样例:
3 6 5 4 1 4
3
输出样例:
4 3 3 3 4 3

#include
using namespace std;
#define int long long
#define fer(i,a,b) for(int i=a;i<b;i++)
bool touzi[6][7];//第i个骰子是否摇出过点j 
signed main(){
	int n=6;int k,tmp;
	fer(i,0,n){
		cin>>tmp;
		touzi[i][tmp]=1;
	}
	cin>>k;
	int res[6]={0};
	fer(j,0,k){
		fer(i,0,n){
			for(int s=6;s>0;s--){
				if(touzi[i][s]==0){
					touzi[i][s]=1;
					if(j==k-1)res[i]=s;
					break;
				} 
			}
		}
	}
	fer(i,0,n){
		if(i!=n-1)cout<<res[i]<<" ";
		else cout<<res[i]<<endl;
	}
	return 0;
}

L1-6 打PTA 15 4995/19214(26.00%)

本题要求你做一个简单的自动问答机,对任何一个问句,只要其中包含 PTA 就回答 Yes!,其他一概回答 No.。

输入格式:
输入第一行给出一个整型范围内的正整数 N,随后 N 行,每行给出一个长度不超过 80 的字符串,为用户输入的句子,由英文字母、数字、空格和标点符号组成,以回车结束。

输出格式:
对每一行句子,如果其结尾字符为问号 ? 则判断此句中有无 PTA?如果有则在一行中输出 Yes!,否则输出 No.。如果不是问号结尾,则敷衍地回答 enen。

输入样例:
5
Hello!
Do you still play WZRY?
Chi Ji?
you play PTA ah?
how about pta site?
输出样例:
enen
No.
No.
Yes!
No.

#include
#include
using namespace std;
#define int long long
#define fer(i,a,b) for(int i=a;i<b;i++)
#define cf int T;cin>>T;while(T--)

signed main(){
	
	
	int T;cin>>T;//char tmp;cin>>tmp;
	string s;
	char tmp;cin>>tmp;
	while(T--){
		getline(cin,s);
		int len=s.size();
		if(s[len-1]!='?')cout<<"enen"<<endl;
		else{
			bool f=0;
			fer(i,0,len-3){
				if(s[i]=='P'&&s[i+1]=='T'&&s[i+2]=='A'){
					f=1;break;
				}
			}
			if(f)cout<<"Yes!"<<endl;
			else cout<<"No."<<endl;
			
		}
	}
	return 0;
}

L1-7 机工士姆斯塔迪奥 20 4579/21562(21.24%)

#include
using namespace std;
#define int long long
#define fer(i,a,b) for(int i=a;i<b;i++)
#define cf int T;cin>>T;while(T--)

signed main(){
	
	
	int n,m,q;cin>>n>>m>>q;
	int cntn=0,cntm=0;
	set<int>cn,cm;
	while(q--){
		int a,b;cin>>a>>b;
		if(a==0){
			if(cn.find(b)==cn.end()){
				cn.insert(b);cntn++;
			}
		}
		else{
			if(cm.find(b)==cm.end()){
				cm.insert(b);cntm++;
			}
		}
	}
	cout<<(n-cntn)*(m-cntm)<<endl;
	return 0;
}

L1-8 随机输一次 20 4708/11645(40.43%)

#include
using namespace std;
#define int long long
#define fer(i,a,b) for(int i=a;i<b;i++)
#define cf int T;cin>>T;while(T--)
void win(string enemy){
	if(enemy=="ChuiZi")cout<<"Bu"<<endl;
	else if(enemy=="JianDao")cout<<"ChuiZi"<<endl;
	else cout<<"JianDao"<<endl;
}
void lose(string enemy){
	if(enemy=="ChuiZi")cout<<"JianDao"<<endl;
	else if(enemy=="JianDao")cout<<"Bu"<<endl;
	else cout<<"ChuiZi"<<endl;
}
signed main(){
	
	int n;cin>>n;
	int rdm[n];
	fer(i,0,n)cin>>rdm[i];
	string enemy;
	int cnt=0,k=rdm[0],ki=0;
	while(cin>>enemy){
		if(enemy=="End")break;
		if(cnt!=k){
			win(enemy);
			cnt++;
		}else{
			lose(enemy);
			cnt=0;
			ki++;
			if(ki==n)ki=0;
			k=rdm[ki];
		}
	}
	return 0;
}

L2-1 插松枝 25 1150/7842(14.66%)

人造松枝加工场的工人需要将各种尺寸的塑料松针插到松枝干上,做成大大小小的松枝。他们的工作流程(并不)是这样的:

每人手边有一只小盒子,初始状态为空。
每人面前有用不完的松枝干和一个推送器,每次推送一片随机型号的松针片。
工人首先捡起一根空的松枝干,从小盒子里摸出最上面的一片松针 —— 如果小盒子是空的,就从推送器上取一片松针。将这片松针插到枝干的最下面。
工人在插后面的松针时,需要保证,每一步插到一根非空松枝干上的松针片,不能比前一步插上的松针片大。如果小盒子中最上面的松针满足要求,就取之插好;否则去推送器上取一片。如果推送器上拿到的仍然不满足要求,就把拿到的这片堆放到小盒子里,继续去推送器上取下一片。注意这里假设小盒子里的松针片是按放入的顺序堆叠起来的,工人每次只能取出最上面(即最后放入)的一片。
当下列三种情况之一发生时,工人会结束手里的松枝制作,开始做下一个:
(1)小盒子已经满了,但推送器上取到的松针仍然不满足要求。此时将手中的松枝放到成品篮里,推送器上取到的松针压回推送器,开始下一根松枝的制作。

(2)小盒子中最上面的松针不满足要求,但推送器上已经没有松针了。此时将手中的松枝放到成品篮里,开始下一根松枝的制作。

(3)手中的松枝干上已经插满了松针,将之放到成品篮里,开始下一根松枝的制作。

现在给定推送器上顺序传过来的 N 片松针的大小,以及小盒子和松枝的容量,请你编写程序自动列出每根成品松枝的信息。

输入格式:
输入在第一行中给出 3 个正整数:N(≤10
3
),为推送器上松针片的数量;M(≤20)为小盒子能存放的松针片的最大数量;K(≤5)为一根松枝干上能插的松针片的最大数量。

随后一行给出 N 个不超过 100 的正整数,为推送器上顺序推出的松针片的大小。

输出格式:
每支松枝成品的信息占一行,顺序给出自底向上每片松针的大小。数字间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:
8 3 4
20 25 15 18 20 18 8 5
输出样例:
20 15
20 18 18 8
25 5

L2-2 老板的作息表 25 2300/10279(22.38%)

本题就请你编写程序,检查任意一张时间表,找出其中没写出来的时间段。

输入格式:
输入第一行给出一个正整数 N,为作息表上列出的时间段的个数。随后 N 行,每行给出一个时间段,格式为:

hh:mm:ss - hh:mm:ss
其中 hh、mm、ss 分别是两位数表示的小时、分钟、秒。第一个时间是开始时间,第二个是结束时间。题目保证所有时间都在一天之内(即从 00:00:00 到 23:59:59);每个区间间隔至少 1 秒;并且任意两个给出的时间区间最多只在一个端点有重合,没有区间重叠的情况。

输出格式:
按照时间顺序列出时间表中没有出现的区间,每个区间占一行,格式与输入相同。题目保证至少存在一个区间需要输出。

输入样例:
8
13:00:00 - 18:00:00
00:00:00 - 01:00:05
08:00:00 - 09:00:00
07:10:59 - 08:00:00
01:00:05 - 04:30:00
06:30:00 - 07:10:58
05:30:00 - 06:30:00
18:00:00 - 19:00:00
输出样例:
04:30:00 - 05:30:00
07:10:58 - 07:10:59
09:00:00 - 13:00:00
19:00:00 - 23:59:59

#include
using namespace std;
#define int long long
#define fer(i,a,b) for(int i=a;i<b;i++)
#define cf int T;cin>>T;while(T--)
struct node{
	string sa,sb;
	int ah,am,as,bh,bm,bs;
};
bool cmp(node &p,node &q){
	if(p.ah==q.ah){
		if(p.am==q.am){
			return p.as<q.as;
		}else return p.am<q.am;
	}else return p.ah<q.ah;
}
void print(int a,int b,int c){
	if(a<10)cout<<0<<a;
	else cout<<a;
	cout<<":";
	if(b<10)cout<<0<<b;else cout<<b;
	cout<<":";
	if(c<10)cout<<0<<c;else cout<<c;
}
signed main(){
	int n;cin>>n;
	node boss[n];
	fer(i,0,n){
		cin>>boss[i].sa;
		string t;cin>>t;
		cin>>boss[i].sb;
		boss[i].ah=(boss[i].sa[0]-'0')*10+boss[i].sa[1]-'0';
		boss[i].am=(boss[i].sa[3]-'0')*10+boss[i].sa[4]-'0';
		boss[i].as=(boss[i].sa[6]-'0')*10+boss[i].sa[7]-'0';
		
		boss[i].bh=(boss[i].sb[0]-'0')*10+boss[i].sb[1]-'0';
		boss[i].bm=(boss[i].sb[3]-'0')*10+boss[i].sb[4]-'0';
		boss[i].bs=(boss[i].sb[6]-'0')*10+boss[i].sb[7]-'0';
	}
	sort(boss,boss+n,cmp);
	
//	cout<
//	fer(i,0,n){
//		print(boss[i].ah,boss[i].am,boss[i].as);cout<<" - ";
//		print(boss[i].bh,boss[i].bm,boss[i].bs);cout<
//	}
//	cout<
	
	int nowh=0,nowm=0,nows=0;
	fer(i,0,n){
		
		if(boss[i].ah!=nowh||boss[i].am!=nowm||boss[i].as!=nows){
			print(nowh,nowm,nows);
			cout<<" - ";
			nowh=boss[i].ah;nowm=boss[i].am;nows=boss[i].as;
//			if(nows<0){nowm--;nows=59;}
//			if(nowm<0){nowh--;nowm=59;}
			
			print(nowh,nowm,nows);cout<<endl;
			
			nowh=boss[i].bh;nowm=boss[i].bm;nows=boss[i].bs;
//			if(nows>59){nowm++;nows=0;}
//			if(nowm>59){nowh++;nowm=0;}
		}else{
			nowh=boss[i].bh;nowm=boss[i].bm;nows=boss[i].bs;
//			if(nows>59){nowm++;nows=0;}
//			if(nowm>59){nowh++;nowm=0;}
		}
	}
	if(nowh!=23||nowm!=59||nows!=59){
		print(nowh,nowm,nows);cout<<" - ";
		print(23,59,59);cout<<endl;
	}
	return 0;
}

L2-3 智能护理中心统计 25 416/4802(8.66%)

智能护理中心系统将辖下的护理点分属若干个大区,例如华东区、华北区等;每个大区又分若干个省来进行管理;省又分市,等等。我们将所有这些有管理或护理功能的单位称为“管理结点”。现在已知每位老人由唯一的一个管理结点负责,每个管理结点属于唯一的上级管理结点管辖。你需要实现一个功能,来统计任何一个管理结点所负责照看的老人的数量。

注意这是一个动态问题,即随时可能有老人加入某个管理结点,并且老人是有可能从一个管理结点换到另一个管理结点去的。

输入格式:
输入在第一行中给出 2 个正整数:N(≤10
4
)是老人的总数量,即老人们从 1 到 N 编号;M(≤10
5
)是归属关系的总数。

接下来是 M 行,每行给出一对归属关系,格式为:

A B
表示 A 归属于 B。A 或 B 如果是某个管理结点,则用不超过 4 个大写英文字母表示其名称;如果是某位老人,则用老人的编号表示。这里每个 A 保证只有唯一的上级归属 B,且只有这个中心系统本身是没有上级归属的。此外,输入保证没有老人自己承担管理结点的角色,即 B 一定是一个管理结点,不可能是老人的编号。但一个管理结点既可以管辖下级结点,也可以直接护理一部分老人。

随后每行给出一个指令,格式为:

指令 内容
如果 指令 为 T,则表示有老人要入院或转院,内容 是某老人的编号和要去的管理结点的名称,以空格分隔;如果 指令 为 Q,则 内容 是一个管理结点的名称,意思是统计这个结点所负责照看的老人的数量;如果 指令 为 E,则表示输入结束。题目保证指令总数不会超过 100 个。

输出格式:
对每个 T 指令,将对应的老人转存到对应的管理结点名下;对每个 Q 指令,在一行中输出对应管理结点所负责照看的老人的数量。读到 E 指令就结束程序。

输入样例:
10 23
EAST CNTR
ZJ EAST
SD EAST
WEST CNTR
SX WEST
HZ ZJ
JN SD
2 JN
8 JTH
6 XAHP
4 ZDYH
5 ZDYH
ZDYH HZ
HUZ ZJ
JX ZJ
1 JX
3 JN
WZ ZJ
XAHP XIAN
XIAN SX
YL SX
JTH XIAN
7 XAHP
Q EAST
T 1 YL
Q EAST
Q SX
T 8 ZDYH
Q HZ
Q HUZ
T 10 XAHP
Q CNTR
E
输出样例:
5
4
4
3
0
9

L2-4 大众情人 25 518/4468(11.59%)

人与人之间总有一点距离感。我们假定两个人之间的亲密程度跟他们之间的距离感成反比,并且距离感是单向的。例如小蓝对小红患了单相思,从小蓝的眼中看去,他和小红之间的距离为 1,只差一层窗户纸;但在小红的眼里,她和小蓝之间的距离为 108000,差了十万八千里…… 另外,我们进一步假定,距离感在认识的人之间是可传递的。例如小绿觉得自己跟小蓝之间的距离为 2,则即使小绿并不直接认识小红,我们也默认小绿早晚会认识小红,并且因为跟小蓝很亲近的关系,小绿会觉得自己跟小红之间的距离为 1+2=3。当然这带来一个问题,如果小绿本来也认识小红,或者他通过其他人也能认识小红,但通过不同渠道推导出来的距离感不一样,该怎么算呢?我们在这里做个简单定义,就将小绿对小红的距离感定义为所有推导出来的距离感的最小值。

一个人的异性缘不是由最喜欢他/她的那个异性决定的,而是由对他/她最无感的那个异性决定的。我们记一个人 i 在一个异性 j 眼中的距离感为 D
ij

;将 i 的“异性缘”定义为 1/max
j∈S(i)

{D
ij

},其中 S(i) 是相对于 i 的所有异性的集合。那么“大众情人”就是异性缘最好(值最大)的那个人。

本题就请你从给定的一批人与人之间的距离感中分别找出两个性别中的“大众情人”。

输入格式:
输入在第一行中给出一个正整数 N(≤500),为总人数。于是我们默认所有人从 1 到 N 编号。

随后 N 行,第 i 行描述了编号为 i 的人与其他人的关系,格式为:

性别 K 朋友1:距离1 朋友2:距离2 …… 朋友K:距离K
其中 性别 是这个人的性别,F 表示女性,M 表示男性;K( 6
的正整数。

题目保证给出的关系中一定两种性别的人都有,不会出现重复给出的关系,并且每个人的朋友中都不包含自己。

输出格式:
第一行给出自身为女性的“大众情人”的编号,第二行给出自身为男性的“大众情人”的编号。如果存在并列,则按编号递增的顺序输出所有。数字间以一个空格分隔,行首尾不得有多余空格。

输入样例:
6
F 1 4:1
F 2 1:3 4:10
F 2 4:2 2:2
M 2 5:1 3:2
M 2 2:2 6:2
M 2 3:1 2:5
输出样例:
2 3
4

L3-1 塔防游戏 30 5/967(0.52%)

有一种简单的塔防游戏是这样的:给定一张由 n 行 m 列个方格子构成的地图,玩家可以任选一个格子放置自己的大本营,还可以在任意一个格子里放置自己的防御堡垒。大本营和每个防御堡垒都有自己的防御能力值 d,表示可以抵御 d 个僵尸的攻击。每一轮游戏开始时,玩家在规定时间内将本级别可以用的防御堡垒布置在地图中,然后僵尸们就从地图边界涌入地图中,向着大本营发起攻击。每轮进攻持续一个固定的时长,结束后剩余的僵尸就原地蒸发。

每队僵尸可以向一个方格的上下左右四个方向移动。如果相邻的目标方格没有堡垒,它们就可以用 1 秒的时间移动过去,否则会被堡垒阻挡或者消灭。对每一队僵尸(从同一地点出发的所有僵尸)而言,每秒会被堡垒消灭 1 个队友,同时消耗掉该堡垒 1 个单位的防御能力。当防御能力降为 0,则该堡垒消失,剩下的僵尸则用 1 秒移动到这个方格继续行进。注意:如果有多支僵尸队都进入了同一个方格,它们并不会合并成一支队伍。

所有的僵尸队都会根据进攻开始时的地图选择被歼灭最少的到达大本营的路线,并且一直按照这个路线行进,中途不因为地图状态的改变而改变。当这样的进攻路径不唯一时,选择能最快到达大本营的路径。题目保证这样的路径所打掉的堡垒的布局是唯一的。

本题就要求你计算出一轮攻击结束时,地图上的布局情况。

输入格式:
输入首先在第一行中给出三个正整数:不超过 100 的 n 和 m,为地图的尺寸;不超过 1000 的 T,为一轮攻击持续的时长。

随后给出 n+2 行,每行给出 m+2 个数字,每行中的数字都用空格分隔,表示攻击开始前地图上的布局。其中第 1 行、第 1 列、第 n+2 行、第 m+2 列是地图边界外僵尸们出发的位置,这些位置上,0 表示没有僵尸,其他正整数表示从该位置出发的僵尸们的数量。而地图中的每个位置上,0 表示没有堡垒,其它正整数表示该位置上堡垒的防御能力值。大本营是一个特殊的建筑,我们用一个负数 −D 表示这里是大本营,其防御能力值为 D。这里的防御值和任一队僵尸的数量都不超过 100。

注意:僵尸不可在地图边界外移动,它们的第一个移动目标必须在地图中,所以四个角落里出现的僵尸可以被忽略,因为它们没有进入地图的途径。

输出格式:
输出 n 行,每行 m 个数字,对应攻击结束后地图上每个方格的状态。状态的表示与输入相同:没有堡垒的地方输出 0,有堡垒的地方输出其剩余防御值,大本营的位置上输出其剩余防御值的负值。

注意每行数字间以 1 个空格分隔,行首尾不得有多余空格。

当大本营被攻陷时,游戏即刻结束。此时应输出结束时的地图状态,并且在最后一行输出一句 Game Over。

输入样例 1:
7 5 17
0 0 0 0 13 0 0
0 0 0 0 0 0 0
0 0 0 8 0 0 0
0 0 0 0 2 1 0
0 0 0 7 5 3 0
8 0 1 4 -10 1 0
0 0 0 3 3 0 0
0 0 8 0 9 0 0
0 0 0 4 0 0 0
输出样例 1:
0 0 0 0 0
0 0 8 0 0
0 0 0 2 0
0 0 7 5 0
0 0 0 -1 0
0 0 0 2 0
0 8 0 9 0
样例说明:
地图布局如下图所示。

map.JPG

规模为 13 和 8 的两队僵尸都有两种选择,攻打蓝色或者紫色堡垒都是消耗最少的。在这种情况下,规模为 13 的僵尸队走蓝色比较快,需要 1+1+1+2+4+2=11 秒到达大本营边上;规模为 8 的僵尸队走紫色比较快,需要 1+2+5=8 秒到达大本营边上。

规模为 4 的僵尸队比较惨,只能选择绿色堡垒,最后被大本营边上的绿色堡垒消灭。注意到在攻击过程中,其实它们可以等到紫色堡垒被攻陷之后走紫色原始值为 4 的方格,但是因为路径是在初始状态下选定就不能改的,所以它们不能这样选择。

攻打大本营时,规模为 8 的僵尸队剩下了 3 只先到达,在第 11 秒被大本营消灭。此时大本营还剩 7 个单位的防御值,同时规模为 13 的僵尸队剩下的 8 只进入了大本营相邻的方格,开始攻击。但此时距离本轮结束只剩 6 秒,结果大本营在结束时还剩 1 个单位的防御值,玩家胜。

输入样例 2:
7 5 20
0 0 0 0 13 0 0
0 0 0 0 0 0 0
0 0 0 8 0 0 0
0 0 0 0 2 1 0
0 0 0 7 5 3 0
8 0 1 4 -10 1 0
0 0 0 3 3 0 0
0 0 8 0 9 0 0
0 0 0 4 0 0 0
输出样例 2:
0 0 0 0 0
0 0 8 0 0
0 0 0 2 0
0 0 7 5 0
0 0 0 0 0
0 0 0 2 0
0 8 0 9 0
Game Over
样例说明:
样例 2 与样例 1 唯一的区别在于攻击时长变为 20。但实际上,攻击在第 18 秒就结束了。

L3-2 关于深度优先搜索和逆序对的题应该不会很难吧这件事 30 75/7238(1.04%)

背景知识
深度优先搜索与 DFS 序
深度优先搜索算法(DFS)是一种用于遍历或搜索树或图的算法。以下伪代码描述了在树 T 上进行深度优先搜索的过程:

procedure DFS(T, u, L) // T 是被深度优先搜索的树
// u 是当前搜索的节点
// L 是一个链表,保存了所有节点被第一次访问的顺序
append u to L // 将节点 u 添加到链表 L 的末尾
for v in u.children do // 枚举节点 u 的所有子节点 v
DFS(T, v) // 递归搜索节点 v
令 r 为树 T 的根,调用 DFS(T, r, L) 即可完成对 T 的深度优先搜索,保存在链表 L 中的排列被称为 DFS 序。相信聪明的你已经发现了,如果枚举子节点的顺序不同,最终得到的 DFS 序也会不同。

逆序对
给定一个长度为 n 的整数序列 a
1

,a
2

,⋯,a
n

,该序列的逆序对数量是同时满足以下条件的有序数对 (i,j) 的数量:

1≤i a
i

a
j


问题求解
给定一棵 n 个节点的树,其中节点 r 为根。求该树所有可能的 DFS 序中逆序对数量之和。

输入格式
第一行输入两个整数 n,r(2≤n≤3×10
5
,1≤r≤n)表示树的大小与根节点。

对于接下来的 (n−1) 行,第 i 行输入两个整数 u
i

与 v
i

(1≤u
i

,v
i

≤n),表示树上有一条边连接节点 u
i

与 v
i

输出格式
输出一行一个整数,表示该树所有可能的 DFS 序中逆序对数量之和。由于答案可能很大,请对 10
9
+7 取模后输出。

样例输入 1
5 3
1 5
2 5
3 5
4 3
样例输出 1
24
样例输入 2
10 5
10 2
2 5
10 7
7 1
7 9
4 2
3 10
10 8
3 6
样例输出 2
516
样例解释
下图展示了样例 1 中的树。

sample.png

该树共有 4 种可能的 DFS 序:

{3,4,5,1,2},有 6 个逆序对;
{3,4,5,2,1},有 7 个逆序对;
{3,5,1,2,4},有 5 个逆序对;
{3,5,2,1,4},有 6 个逆序对。
因此答案为 6+7+5+6=24。

L3-3 教科书般的亵渎

九条可怜最近在玩一款卡牌游戏。在每一局游戏中,可怜都要使用抽到的卡牌来消灭一些敌人。每一名敌人都有一个初始血量,而当血量降低到 0 及以下的时候,这名敌人就会立即被消灭并从场上消失。

现在,可怜面前有 n 个敌人,其中第 i 名敌人的血量是 a
i

,而可怜手上只有如下两张手牌:

如果场上还有敌人,等概率随机选中一个敌人并对它造成一点伤害(即血量减 1),重复 K 次。

对所有敌人造成一点伤害,重复该效果直到没有新的敌人被消灭。

下面是这两张手牌效果的一些示例:

假设存在两名敌人,他们的血量分别是 1,2 且 K=2。那么在可怜打出第一张手牌后,可能会发生如下情况:
第一轮中,两名敌人各有 0.5 的概率被选中。假设第一名敌人被选中,那么它会被造成一点伤害。这时它的血量变成了 0,因此它被消灭并消失了。
第二轮中,因为场上只剩下了第二名敌人,所以它一定会被选中并被造成一点伤害。这时它剩下的血量为 1。
同样假设存在两名敌人且血量分别为 1,2。那么在可怜打出第二张手牌后,会发生如下情况:
第一轮中,所有敌人被造成了一点伤害。这时第一名敌人被消灭了,因此卡牌效果会被重复一遍。
第二轮中,所有敌人(此时只剩下第二名敌人了)被造成了一点伤害。这时第二名敌人也被消灭了,因此卡牌效果会被再重复一遍。
第三轮中,所有敌人(此时没有敌人剩下了)被造成了一点伤害。因为没有新的敌人被消灭了,所以卡牌效果结束。
如果面对的是四名血量分别为 1,2,2,4 的敌人,那么在可怜打出第二张手牌后,只有第四名敌人还会存活,且它的剩余血量为 1。
现在,可怜先打出了第一张手牌,再打出了第二张手牌。她发现,在第一张手牌效果结束后,没有任何一名敌人被消灭,但是在第二张手牌的效果结束后,所有敌人都被消灭了。

可怜想让你计算一下这种情况发生的概率是多少。

输入格式:
第一行输入两个整数 n,K(1≤n,K≤50),分别表示敌人的数量以及第一张卡牌效果的发动次数。

第二行输入 n 个由空格隔开的整数 a
i

(1≤a
i

≤50),表示每个敌人的初始血量。

输出格式:
在一行中输出一个整数,表示发生概率对 998244353 取模后的结果。

具体来说,如果概率的最简分数表示为 a/b(a≥0,b≥1,gcd(a,b)=1),那么你需要输出

a×b
998244351
mod998244353。

输入样例 1:
3 2
2 3 3
输出样例 1:
665496236
样例解释 1:
在第一张手牌的效果结束后,三名敌人的剩余血量只可能在如下几种中:[1,3,2], [1,2,3], [2,1,3] 和 [2,3,1]。前两种发生的概率是 2/9,后两种发生的概率是 1/9。因此答案为 2/3,输出 2×3
998244351
mod998244353=665496236。

输入样例 2:
3 3
2 3 3
输出样例 2:
776412275
样例解释 2:
在第一张手牌的效果结束后,三名敌人的剩余血量只可能在如下几种中:[1,2,2]、[2,1,2] 和 [2,2,1]。第一种发生的概率是 2/9,后两种发生的概率是 1/9。因此答案为 4/9,输出 4×9
998244351
mod998244353=776412275。

输入样例 3:
5 3
1 4 4 2 5
输出样例 3:
367353922
输入样例 4:
12 12
1 2 3 4 5 6 7 8 9 10 11 12
输出样例 4:
452061016

你可能感兴趣的:(c++,算法)