【天梯赛】2026天梯赛模拟赛——题解

去掉L2-3是本人规定时间范围内真实有效的分数。


L1-1~L1-5

不解释也不放题干,自行看代码。

cpp 复制代码
#include<iostream>
#define i64 long long
using namespace std;
int main(){
	cout<<"I'm gonna WIN!"<<endl;
	cout<<"I'm gonna WIN!"<<endl;
	cout<<"I'm gonna WIN!"<<endl;
	return 0;
}
cpp 复制代码
#include<iostream>
#define i64 long long
using namespace std;
i64 n,a,b;
int main(){
	cin>>a>>b;
	cout<<a/b<<endl;
	return 0;
}
cpp 复制代码
#include<iostream>
#define i64 long long
using namespace std;
i64 n,a,b;
int main(){
	cin>>a;
	if(a<0){
		cout<<a<<endl;
		cout<<"dan ren"<<endl;	
	}else if(a==0){
		cout<<a<<endl;
		cout<<"xian ren"<<endl;
	}else{
		cout<<a<<endl;
		cout<<"nong ren"<<endl;
	}
	return 0;
}
cpp 复制代码
#include<iostream>
#define i64 long long
using namespace std;
i64 n,a,b;
string s,s1,s2,s3;
int main(){
	getline(cin,s);
	while(s!="#"){
		cout<<s<<endl;
		getline(cin,s);
	}
	return 0;
}
cpp 复制代码
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<queue>
#define i64 long long
using namespace std;
i64 n,x,y;
bool flag=0;
bool fun(i64 num);
int main(){
	cin>>y>>n;
	for(i64 i=0;i<=9999&&!flag;i++){
		if(fun(y+i)){
			flag=1; 
		}
	}
	return 0;
}
bool fun(i64 num){
	i64 a[15],cnt=0;
	for(i64 i=0;i<=9;i++){
		a[i]=0;
	}
	a[num/1000%10]++;
	a[num/100%10]++;
	a[num/10%10]++;
	a[num%10]++;
	for(i64 i=0;i<=9;i++){
		if(a[i]!=0){
			++cnt;
		}
	}
	if(cnt==n){
		cout<<num-y<<' '<<num/1000%10<<num/100%10<<num/10%10<<num%10<<endl;
		return 1;
	}
	return 0;
}

L1-6 骗钱的手机游戏

输入样例:

1001 1002 900 899 799 700 601 501 400

输出样例:

1 2 0 0 0 1 0 0 743

类似于一种高精度运算,先除,加到后面一位,再对自己取模。

cpp 复制代码
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<queue>
#define i64 long long
using namespace std;
i64 n,x,y;
i64 a[15];
int main(){
	for(i64 i=1;i<=9;i++){
		cin>>a[i];
	}
	for(i64 i=1;i<9;i++){
		if(i%2==1){
			a[i+1]+=a[i]/2;
			a[i]%=2;
		}else{
			a[i+1]+=a[i]/3;
			a[i]%=3;
		}
	}
	for(i64 i=1;i<=9;i++){
		cout<<a[i];
		if(i!=9) cout<<' ';
	}
	return 0;
}

L1-7 代码查重

输入样例:

9 5

2023-06-01 10:23:10 3 12

2023-06-01 10:53:09 3 18

2023-06-01 11:23:12 1 1

2023-06-01 11:28:15 3 10

2023-06-01 11:43:42 3 18

2023-06-01 11:53:12 5 0

2023-06-01 12:11:20 1 5

2023-06-01 13:25:13 4 20

2023-06-01 14:03:57 1 2

输出样例:

2023-06-01 10:53:09 3 18

2023-06-01 11:53:12 5 0

2023-06-01 12:11:20 1 5

2023-06-01 13:25:13 4 20

结构体排序。为了方便运算,建议多定义两个数据------数据序号、是否显示。

排序顺序是先按照问题排序,对于同一个问题,分数优先,对于同分,序号(时间)优先。

然后去顺序遍历这些数据,可以确定,每个题号的第一条数据是需要显示的数据,因此isr标上1。

然后按照输入顺序重新排一遍,如果isr==1那么就输出对应的数据。

cpp 复制代码
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<queue>
#define i64 long long
using namespace std;
i64 n,q;
struct node{
	string dat,tms;
	i64 num,que,sco;
	bool isr;
}a[200005];
bool cmp1(struct node x,struct node y){
	if(x.que==y.que){
		if(x.sco==y.sco){
			return x.num<y.num;
		}
		return x.sco>y.sco;
	}
	return x.que<y.que;
}
bool cmp2(struct node x,struct node y){
	return x.num<y.num;
}
int main(){
	cin>>n>>q;
	for(i64 i=1;i<=n;i++){
		cin>>a[i].dat>>a[i].tms>>a[i].que>>a[i].sco;
		a[i].isr=0;
		a[i].num=i;
	}
	sort(a+1,a+n+1,cmp1);
	for(i64 i=1;i<=n;i++){
		if(a[i].que!=a[i-1].que){
			a[i].isr=1;
		}
	}
	sort(a+1,a+n+1,cmp2);
	for(i64 i=1;i<=n;i++){
		if(a[i].isr==1)
			cout<<a[i].dat<<' '<<a[i].tms<<' '<<a[i].que<<' '<<a[i].sco<<endl;
	}
	return 0;
}

L1-8 迎新字符串

输入样例:

7

backtothefuture

1 the

2 the that

1 atfu

1 the

2 ture fure

3 f u fuwawa

1 wa

输出样例:

6

backtothatfuture

8

-1

backtothatfufure

backtothatffuwawauffuwawaure

13

我自己写的样例:

1

backtotheffuture

3 f u fuwawa

1

ababc

3 a b x

第一次交13分,写的又臭又长,AI教我之后喜提20分。

s.find(str, pos)、s.replace(pos, len, str)、s.insert(pos, str)这三个函数不会用可以重开了。

此外常见的还有s.erase(pos, len)、s.substr(pos, len)。

算法竞赛 常用的字符串函数-CSDN博客

现在最难的就是第三个操作,注意两个点:第一是"所有",第二是"操作不必考虑 s4​ 插入后出现的新的可插入的位置"。所以第三个循环跑不掉一个循环。

我在这里写while循环,这样就能保证能更新字符串长度。如果扫描到了,那么使用s.insert要注意,起始位置是j+1,因为j是c1,你不能破坏它。然后终止你的j对应的位置应该是插入s3的最后一个,自己算一下就知道了。

cpp 复制代码
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<queue>
#define i64 long long
using namespace std;
i64 n,op;
string s,s1,s2,s3;
char c1,c2;
int main(){
	cin>>n>>s;
	for(i64 i=1;i<=n;i++){
		cin>>op;
		if(op==1){
			cin>>s1;
			if(s.find(s1)==string::npos) cout<<-1<<endl;
			else cout<<s.find(s1)<<endl;
		}else if(op==2){
			cin>>s1>>s2;
			i64 pos=s.find(s1);
			if(pos!=string::npos)
				s.replace(pos,s1.length(),s2);
			cout<<s<<endl;
		}else if(op==3){
			cin>>c1>>c2>>s3;//吃掉空格
			//cout<<c1<<" "<<c2<<" "<<s3<<endl; 
			bool flag=1;
			i64 j=0;
			while(j<s.length()){
				if(s[j]==c1&&s[j+1]==c2){
					s.insert(j+1,s3);
					j=j+1+s3.length()-1;
				}	
				j++;
			}
			cout<<s<<endl;	
		}
	}
	return 0;
}

L2-1 简单计算器

建议多来点这种题目,分数会好看的一批(滑稽)。

这题说要一个栈,但是实际上完全没必要,这题都能放在L1-5的位置。代码不解释了。

cpp 复制代码
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<queue>
#define i64 long long
using namespace std;
i64 n,q;
i64 stk[100005];
char s[100005];
int main(){
	cin>>n;
	for(i64 i=1;i<=n;i++){
		cin>>stk[i];
	}
	for(i64 i=1;i<n;i++){
		cin>>s[i];
	}
	i64 flag=0;
	for(i64 i=n-1;i>=1&&!flag;i--){
		i64 n1=stk[i],n2=stk[i+1];
		if(s[i]=='+'){
			stk[i]=n1+n2;
		}else if(s[i]=='-'){
			stk[i]=n1-n2; 
		}else if(s[i]=='*'){
			stk[i]=n1*n2;
		}else if(s[i]=='/'){
			if(n2!=0)
				stk[i]=n1/n2;
			else{
				cout<<"ERROR: "<<n1<<"/0"<<endl;
				flag=1;	
			}
		}
	}
	if(!flag) cout<<stk[1]<<endl;
	return 0;
}

L2-2 为i做e

输入样例 1:

10

00000000 i

12345678 e

23468270 i

78827341 e

67476289 i

35748108 e

99999999 i

40926483 i

88472901 i

55032849 i

3

3 00000000 67476289 99999999

4 12345678 78827341 35748108 55032849

3 23468270 40926483 88472901

输出样例 1:

1 3

输入样例 2:

10

00000000 i

12345678 e

23468270 i

78827341 e

67476289 i

35748108 e

99999999 i

40926483 i

88472901 i

55032849 i

2

4 78827341 35748108 55032849 00000000

6 12345678 67476289 99999999 23468270 40926483 88472901

输出样例 2:

None

这题本身比较繁琐,但是不难。

首先定义结构体,大家都想得到,但是到了后面这个饭桌,我怎么去一个个查谁到底是i是e呢?就要用到二分查找lower_bound()。

但是你会问了:结构体不支持二分查找这个函数啊?所以我们可以灵机一动,空间换时间,再给你开一个纯粹的数组,你对它二分查找就行了,查出来的结果映射到结构体上。

如果有e人,那么flag置1,整个饭局都没有e人,那么flag无法置1,存到结果数组里即可。

cpp 复制代码
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<queue>
#define i64 long long
using namespace std;
i64 n,q,r,t;
i64 stk[100005],aa[100005],top=0;
struct node{
	i64 num;
	char ch;
}a[100005];
bool cmp(struct node x, struct node y){
	return x.num<y.num;
}
int main(){
	cin>>n;
	for(i64 i=1;i<=n;i++){
		cin>>a[i].num>>a[i].ch;
		aa[i]=a[i].num;
	}
	sort(a+1,a+n+1,cmp);
	sort(aa+1,aa+n+1);
	cin>>q;
	for(i64 i=1;i<=q;i++){
		cin>>r;
		bool flag=0;
		for(i64 j=1;j<=r;j++){
			cin>>t;
			i64 pos=lower_bound(aa+1,aa+n+1,t)-aa;
			//cout<<pos<<endl;
			if(a[pos].ch=='e'){
				//cout<<i<<' '<<pos<<' '<<a[pos].num<<endl;
				flag=1;
			}
		}
		if(!flag){
			stk[++top]=i;
		}
	}
	if(top!=0){
		for(i64 i=1;i<=top;i++){
			cout<<stk[i];
			if(i!=top)
				cout<<' ';
		}
	}
	else cout<<"None";
	return 0;
}

L2-3 自然倍树

输出格式:

对每组测试,在一行中输出 1,如果对应的树不是自然倍树,否则输出 0

输入样例:

2

6

10 20 6 9 18 13

10 13 20 6 18 9

6

6 9 10 20 18 13

6 13 9 10 18 20

输出样例:

1

0

被数据结构单杀了。。。这题就是给定后序中序求前序,如果你不清楚三个序列,请移步自行补充相关知识(你去找一个动画演示就学会了)。

AI点拨------思路是要用递归,恍然大悟。我们可以这么来看待这题:

后序数组:左子树 右子树 根

中序数组:左子树 根 右子树

那么对于后序数组的最后一个位置,你去找中序数组跟它相等的数据,记录下位置来。然后递归分别进入左子树和右子树,重复这个操作,那么就能得到一个前序数组了。

然后这题要求每层的和,那么我们就递归一个depth,把它记录进去就可以了。c数组是前序,d数组是层次,e数组是同层之和。

递归的前四个参数是很不好理解的,也是这个数据结构的精华所在。具体的理解和写代码的注意事项我全放在注释里面了。

cpp 复制代码
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<cmath>
#define i64 long long
using namespace std;
i64 n,t,cnt=0;
i64 a[35],b[35],c[35],d[35],e[35],top=0;
void dfs(i64 hl,i64 hr,i64 zl,i64 zr,i64 depth);
int main(){
	cin>>t;
	while(t--){
		top=0;
		for(i64 i=1;i<=n;i++){
			a[i]=0;
			b[i]=0;
			c[i]=0;
			d[i]=0;
			e[i]=0;
		}
		cin>>n;
		for(i64 i=1;i<=n;i++){
			cin>>a[i];
		}
		for(i64 i=1;i<=n;i++){
			cin>>b[i];
		}
		dfs(1,n,1,n,1);
		for(i64 i=1;i<=n;i++){
			e[d[i]]+=c[i];
		}
		i64 flag=1;
		for(i64 i=1;i<=30;i++){
			if(e[i]%i!=0){
				flag=0;
			}
		}
		cout<<flag<<endl;
	} 
	return 0;
}
void dfs(i64 hl,i64 hr,i64 zl,i64 zr,i64 depth){
	if(hl>hr){
		return;
	}
	//这一整段的尾数为a[hr] 
	i64 num=a[hr];
	c[++top]=a[hr];
	d[top]=depth; 
	//cout<<a[hr]<<endl;
	
	if(hl==hr) return ;
	//找出中序位置并分割,这里不能用二分查找,因为tmd是乱序。 
	i64 pos=zl;
	for(i64 i=zl;i<=zr;i++){
		if(b[i]==num){
			pos=i;
			break;
		}
	} 
	i64 ltnum=pos-zl;//中序的左边才是数量,你不能直接用hl+pos 
	//对于后序遍历,最后一个是中心点;对于中序遍历,pos是中心点 
	//然后,你的分段的左边是l,右边是r,所以新的段收尾不能想当然是1和n-1 
	dfs(hl,hl+ltnum-1,zl,pos-1,depth+1);
	dfs(hl+ltnum,hr-1,pos+1,zr,depth+1);
	return ;
}

L2-4 吉利矩阵

一眼剪枝。剪枝是需要慢慢试探补全的。

遇到这类题,先把dfs逻辑给做出来,做完应该就是19分。

然后我们可以发现:每行的最后一个数你不需要0~L一一试得去,你可以把同一行前面的求和记为tmp,然后用L减掉,给他算出来(如果小于0那么return掉)。(要点1)

接着还可以进一步优化:每行后面的数都可以按照上面的思路来,后面的数减去tmp,然后用L减掉,for循环的就应该是0~L-tmp。这样你就拿到23分了,最长一个测试点267ms。(要点2)

接着我发现还可以做一步优化:你还可以把竖着的也这么操作一遍,这样最长一个测试点降到42ms,但是分数还是23分。(要点3)

最终,我没招了,结果没想到AI告诉我的是------你把同一列前面的求和记为tmp,然后用L减掉------这tm不就是要点1的对称方案吗,我都想到了2的对称方案没去重新思考1的。。。(要点4)

结局:原先最长的一个测试点降到8ms,原先TLE的测试点降到338ms,AC了。

算法复杂度分析:

cpp 复制代码
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<queue>
#define i64 long long
using namespace std;
i64 n,l,cnt=0;
i64 a[15][15];
void dfs(i64 depth);
int main(){
	cin>>l>>n;
	dfs(0);
	cout<<cnt<<endl;
	return 0;
}
void dfs(i64 depth){
	if(depth==n*n){
		bool flag=1;
		for(i64 j=1;j<=n;j++){
			i64 tmp=0;
			for(i64 i=1;i<=n;i++){
				tmp+=a[i][j];
			}
			if(l!=tmp){
				//cout<<"竖着不行"<<tmp<<endl;
				flag=0;
				break;
			}
		}
		if(flag) ++cnt;
		return ;
	}
	i64 x=depth/n+1;
	i64 y=depth%n+1;
	//cout<<x<<' '<<y<<endl;
	if(y==n){
		i64 tmp=0;
		for(i64 i=1;i<y;i++){
			tmp+=a[x][i];
		}
		a[x][y]=l-tmp;
		if(a[x][y]>=0) dfs(depth+1);
		else return ;
	}
	else if(x==n){
		i64 tmp=0;
		for(i64 i=1;i<x;i++){
			tmp+=a[i][y];
		}
		a[x][y]=l-tmp;
		if(a[x][y]>=0) dfs(depth+1);
		else return ;
	}
	else{
		i64 tmp1=0,tmp2=0;
		for(i64 i=1;i<y;i++){
			tmp1+=a[x][i];
		}
		for(i64 i=1;i<x;i++){
			tmp2+=a[i][y];
		}
		i64 leftnum1=l-tmp1;
		i64 leftnum2=l-tmp2;
		i64 leftnum=min(leftnum1,leftnum2);
		
		for(i64 i=0;i<=leftnum;i++){
			a[x][y]=i;
			dfs(depth+1);
		}
	}
	return ;
}

L3-1 是不是堆

输入样例:

3 8

98 72 86 60 65 12 23 50

8 38 25 58 52 82 70 60

10 28 15 12 34 9 8 56

输出样例:

Max Heap

50 60 65 72 12 23 86 98

Min Heap

60 58 52 38 82 70 25 8

Not Heap

56 12 34 28 9 8 15 10

差点又被数据结构单杀。我说实话做这题我真不记得堆是什么东西了,也没查资料,是真凭着感觉做的。没想到真给我写对了。

按照我的理解:最大堆指从根沿着顺下来走到叶子,每一条路都是非严格递减的;最小堆则是非严格递增。所以我们把每条路给找出来就行了。

学数据结构有一个规律:没有度数为1的结点时,叶子数等于非叶子数+1;否则叶子数等于非叶子数。这个规律怎么用呢?意味着就只需要用for循环遍历一半,只要查叶子结点就可以了。对于每个叶子结点,用while循环,把自己一步一步÷2,就是从叶子往上找根部的过程。所以用一个while循环判断即可。我的代码巨好理解好吧。

然后就是后序遍历了。我写到这个的时候,被上面惯性思维带下来了,用while套while,一直套,套不下去。然后我回头一想,x序遍历tmd不是用递归做吗?就是cout的位置不一样罢了,这是数据结构基础最基本的内容,想到这里我真的想扇自己一巴掌,太逗比了。

为了防止真有人看不懂,我写一下这个模版吧。

//cout<<a[depth];//前序

houxu(depth*2);

//cout<<a[depth];//中序

houxu(depth*2+1);

//cout<<a[depth];//后序

然后就是润色修改直到符合题意了。这题说实话你知道或者能猜出来堆是啥,也不难。

cpp 复制代码
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<cmath>
#define i64 long long
using namespace std;
i64 n,t,cnt=0;
i64 a[1005];
bool ismaxheap();
bool isminheap();
void houxu(i64 depth);
int main(){
	cin>>t>>n;
	for(i64 iii=1;iii<=t;iii++){
		for(i64 i=1;i<=n;i++){
			cin>>a[i];
		}
		//cout<<ismaxheap()<<' '<<isminheap()<<endl;
		if(ismaxheap()){
			cout<<"Max Heap"<<endl;
		}else if(isminheap()){
			cout<<"Min Heap"<<endl;
		}else{
			cout<<"Not Heap"<<endl;
		}
		houxu(1);
		cout<<endl;
	}
	return 0;
}
bool ismaxheap(){
	for(i64 i=n;i>n/2;i--){
		i64 lst=i;
		i64 nxt=i/2;
		while(nxt!=0){//符号是否是大小根堆? 
			if(a[lst]>a[nxt]){
				return 0;
			}
			lst=nxt;
			nxt=nxt/2;
		}
	}
	return 1;
}
bool isminheap(){
	for(i64 i=n;i>n/2;i--){
		i64 lst=i;
		i64 nxt=i/2;
		while(nxt!=0){//符号是否是大小根堆? 
			if(a[lst]<a[nxt]){
				return 0;
			}
			lst=nxt;
			nxt=nxt/2;
		}
	}
	return 1;
}
void houxu(i64 depth){
	if(depth*2<=n)
		houxu(depth*2);
	if(depth*2+1<=n)
		houxu(depth*2+1);
	cout<<a[depth];
	if(depth!=1) cout<<" ";
	return ;
}
/*
1 12
1 2 3 4 5 6 7 8 9 10 11 12
*/

L3-2 贪心消消乐(未解开)

输入样例:

4

0 2 5 0

9 2 -6 2

-4 1 -4 3

-1 8 0 -2

输出样例:

(1, 2) (2, 4) 15

(3, 1) (3, 1) 5

(4, 2) (4, 3) 5

(2, 4) (2, 4) 2

27

到这里就基本上不属于我的范围了,我就直接拿AI的做题解吧,大家有兴趣就看,没兴趣就找真大佬的题解。

他告诉我这是前缀和,我又想扇自己一巴掌,我怎么把这东西忘了。。。看来太久没练了。

我的问题:

骗分Code,AI生成,仅供参考

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

#define i64 long long
i64 n, mp[105][105], sum[105][105], zero[105][105];

// 更新前缀和:为了判定黑洞和快速算分
void update() {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            sum[i][j] = mp[i][j] + sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1];
            zero[i][j] = (mp[i][j] == 0) + zero[i-1][j] + zero[i][j-1] - zero[i-1][j-1];
        }
    }
}

i64 get_s(int x1, int y1, int x2, int y2, i64 s[105][105]) {
    return s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1];
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) cin >> mp[i][j];

    i64 total = 0;
    while (true) {
        update();
        i64 max_s = -1e18;
        int rx1, ry1, rx2, ry2;
        bool found = false;

        // 优先级:x1 -> y1 -> x2 -> y2
        for (int x1 = 1; x1 <= n; x1++) {
            for (int y1 = 1; y1 <= n; y1++) {
                for (int x2 = x1; x2 <= n; x2++) {
                    for (int y2 = y1; y2 <= n; y2++) {
                        if (get_s(x1, y1, x2, y2, zero) > 0) continue;
                        i64 cur = get_s(x1, y1, x2, y2, sum);
                        if (cur > max_s) {
                            max_s = cur;
                            rx1 = x1; ry1 = y1; rx2 = x2; ry2 = y2;
                            found = true;
                        }
                    }
                }
            }
        }

        if (!found || max_s <= 0) break;

        printf("(%d, %d) (%d, %d) %lld\n", rx1, ry1, rx2, ry2, max_s);
        total += max_s;

        // 模拟掉落
        for (int j = ry1; j <= ry2; j++) {
            int len = rx2 - rx1 + 1;
            for (int i = rx2; i >= 1; i--) {
                mp[i][j] = (i > len) ? mp[i - len][j] : 0;
            }
        }
    }
    cout << total << endl;
    return 0;
}

满分Code,AI生成,仅供参考

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

#define i64 long long
const i64 INF = 1e18;

i64 n;
i64 mp[105][105];
i64 sum[105][105];  // 分值前缀和
i64 zero[105][105]; // 黑洞数量前缀和

// 更新前缀和函数
void update_prefix_sum() {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            sum[i][j] = mp[i][j] + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
            zero[i][j] = (mp[i][j] == 0) + zero[i - 1][j] + zero[i][j - 1] - zero[i - 1][j - 1];
        }
    }
}

// 获取区间和的通用函数
i64 get_sum(int x1, int y1, int x2, int y2, i64 s[105][105]) {
    return s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1];
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> mp[i][j];
        }
    }

    i64 total_score = 0;

    while (true) {
        update_prefix_sum(); // 每一轮掉落后必须重算

        i64 max_s = -INF;
        int rx1, ry1, rx2, ry2;
        bool found = false;

        // 贪心搜索最大矩形
        // 严格按照题目优先级顺序:x1 -> y1 -> x2 -> y2
        for (int x1 = 1; x1 <= n; x1++) {
            for (int y1 = 1; y1 <= n; y1++) {
                for (int x2 = x1; x2 <= n; x2++) {
                    for (int y2 = y1; y2 <= n; y2++) {
                        
                        // 1. 检查是否有黑洞
                        if (get_sum(x1, y1, x2, y2, zero) > 0) continue;

                        // 2. 计算当前得分
                        i64 cur_score = get_sum(x1, y1, x2, y2, sum);

                        // 3. 更新最大值(注意:> 保证了相同得分时保留先发现的,即坐标小的)
                        if (cur_score > max_s) {
                            max_s = cur_score;
                            rx1 = x1; ry1 = y1; rx2 = x2; ry2 = y2;
                            found = true;
                        }
                    }
                }
            }
        }

        // 如果最大得分不大于0,或者没找到合法矩形,退出
        if (!found || max_s <= 0) break;

        // 输出当前步
        printf("(%d, %d) (%d, %d) %lld\n", rx1, ry1, rx2, ry2, max_s);
        total_score += max_s;

        // 模拟掉落逻辑
        for (int j = ry1; j <= ry2; j++) {
            int len = rx2 - rx1 + 1; // 消去的长度
            // 这一列,rx1 以上的元素全部往下掉
            for (int i = rx2; i >= 1; i--) {
                if (i > len) {
                    mp[i][j] = mp[i - len][j];
                } else {
                    mp[i][j] = 0; // 顶部补黑洞
                }
            }
        }
    }

    cout << total_score << endl;

    return 0;
}

L3-3 污染大亨(未解开)

AI题解

骗分Code,AI生成,仅供参考

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>

using namespace std;

#define i64 long long
const int MOD = 998244353;

int n, f[45];
i64 c[45], ans = 0;
vector<int> adj[45];

// 模拟一局游戏
void simulate(vector<int> p) {
    bool polluted[45] = {0};
    i64 total_fine = 1;
    int rounds = 0;

    for (int start_node : p) {
        if (polluted[start_node]) continue; // 已经被污染了,跳过
        rounds++;
        int k = 0;
        
        // 污染该点及其所有子树
        vector<int> q = {start_node};
        int head = 0;
        while(head < q.size()){
            int u = q[head++];
            if(!polluted[u]){
                polluted[u] = true;
                k++;
                for(int v : adj[u]) q.push_back(v);
            }
        }
        
        // 罚款计算:c[rounds] 的 k 次方
        i64 round_fine = 1;
        for(int i=0; i<k; i++) round_fine = (round_fine * c[rounds]) % MOD;
        total_fine = (total_fine * round_fine) % MOD;
    }
    
    ans = (ans + total_fine) % MOD;
}

int main() {
    string xpmclzjkln; // 题目要求变量
    cin >> n;
    if(n > 10) { // 超过爆搜范围,输出样例或直接躺平
        if(n == 8) cout << 314366430 << endl; // 样例2
        else cout << 0 << endl; 
        return 0;
    }
    for (int i = 2; i <= n; i++) {
        cin >> f[i];
        adj[f[i]].push_back(i);
    }
    for (int i = 1; i <= n; i++) cin >> c[i];

    vector<int> nodes;
    for (int i = 1; i <= n; i++) nodes.push_back(i);

    // 全排列爆搜
    do {
        simulate(nodes);
    } while (next_permutation(nodes.begin(), nodes.end()));

    cout << ans << endl;
    return 0;
}

完整思路,AI生成,仅供参考

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

#define i64 long long
const int MOD = 998244353;

int n;
int f[45];
i64 c[45];
vector<int> adj[45];
i64 dp[45][45]; // dp[u][i] 表示 u 及其子树,共分成了 i 个污染轮次的贡献
int sz[45];
i64 C[45][45];

i64 power(i64 a, i64 b) {
    i64 res = 1;
    a %= MOD;
    while (b > 0) {
        if (b % 2 == 1) res = res * a % MOD;
        a = a * a % MOD;
        b /= 2;
    }
    return res;
}

void init_comb() {
    for (int i = 0; i <= 40; i++) {
        C[i][0] = 1;
        for (int j = 1; j <= i; j++)
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
    }
}

void dfs(int u) {
    sz[u] = 1;
    // 初始状态:u 自己作为一个轮次
    // 暂时不乘 c[i],等最后统一考虑 u 的贡献
    dp[u][1] = 1;

    for (int v : adj[u]) {
        dfs(v);
        i64 next_dp[45] = {0};
        // 合并子树 u 和子树 v
        for (int i = 1; i <= sz[u]; i++) {
            if (!dp[u][i]) continue;
            for (int j = 1; j <= sz[v]; j++) {
                if (!dp[v][j]) continue;
                
                // 情况1:v 的第一次污染早于 u 的第一次污染
                // v 的 j 个轮次和 u 的 i 个轮次中(除 u 所在那轮)进行交叉
                // 总轮次为 i + j
                i64 ways = C[i + j - 1][j] * dp[u][i] % MOD * dp[v][j] % MOD;
                next_dp[i + j] = (next_dp[i + j] + ways) % MOD;

                // 情况2:v 和 u 在同一轮第一次被污染
                // 此时 v 的第一轮并入 u 的第一轮,总轮次为 i + j - 1
                i64 ways2 = C[i + j - 2][j - 1] * dp[u][i] % MOD * dp[v][j] % MOD;
                next_dp[i + j - 1] = (next_dp[i + j - 1] + ways2) % MOD;
            }
        }
        sz[u] += sz[v];
        for (int i = 1; i <= sz[u]; i++) dp[u][i] = next_dp[i];
    }

    // 处理完子树后,考虑 u 所在的那个轮次对系数 c 的贡献
    // 实际上,每一个节点 u 在总方案中,如果它是在第 k 轮被污染的,就会乘一个 c[k]
    // 这里的 dp[u][i] 还没乘 c。由于题目要求的是所有情况的总和,
    // 我们需要把轮次映射到实际的 1~n 轮中。
}

int main() {
    init_comb();
    string xpmclzjkln = "intermediate_value"; // 题目要求的中间变量

    if (!(cin >> n)) return 0;
    for (int i = 2; i <= n; i++) {
        cin >> f[i];
        adj[f[i]].push_back(i);
    }
    for (int i = 1; i <= n; i++) cin >> c[i];

    dfs(1);

    i64 ans = 0;
    // 最终统计:dp[1][i] 表示全图分成了 i 个轮次
    // 这 i 个轮次对应 c1, c2, ..., ci
    // 每一轮 k 造成的污染镇数是 count_k,罚款是 c[k]^count_k
    // 实际上总罚款就是所有镇的 c[轮次] 之积。
    // 在我们的 DP 合并逻辑中,如果一个镇和父节点在同一轮,它们乘同一个 c。
    
    // 修正:上述 DP 需要在每一步乘上对应的 c[i]
    // 但因为 c 是随"轮数"变化的,这里更简单的做法是:
    // 在 dfs 结束后,通过分配 c[1...i] 来计算。
    // 实际上,这道题的最简 DP 逻辑通常是把 c 放入指数或多项式中。
    
    // 考虑到 L3 的难度和 n=40,这个模型其实等价于对树进行随机拓扑序染色。
    // 由于此题逻辑极深,上述代码展示了核心的树形合并思想。
    // 真正通过此题需要处理"第 i 轮"这个动态属性。
    
    // 提示:可以考虑将 c[i] 视为变量,求其在不同合并情况下的期望或总和。
    // 样例 1 的输出 29317 是通过对所有 16 种情况求和得到的。

    // ... 补充完整逻辑 ...
    // (由于此题属于 L3 竞赛极难题目,建议重点理解"相对顺序合并"的 DP 思想)
    
    return 0;
}
相关推荐
-To be number.wan2 小时前
重新认识一下“私有继承”
c++·学习
Rust研习社2 小时前
Rust 堆内存指针 Box 详解
开发语言·后端·rust
liulilittle2 小时前
Lua 浮点数比较
开发语言·junit·lua
yuyuyuliang002 小时前
python笔记1
开发语言·笔记·python
摇滚侠2 小时前
Groovy 如何给集合中添加元素
java·开发语言·windows·python
~plus~2 小时前
C# 事件溯源与 CQRS 架构:用 EventStoreDB 打造可靠系统
开发语言·架构·c#
江奖蒋犟2 小时前
【C++】红黑树
开发语言·c++
lclin_20202 小时前
【大恒相机】C++ 设备枚举+打开关闭+启停采集(基础入门)
c++·工业相机·大恒相机·galaxysdk
雒珣2 小时前
Qt实现命令行参数功能示例:QCommandLineParser
开发语言·数据库·qt