去掉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)。
现在最难的就是第三个操作,注意两个点:第一是"所有",第二是"操作不必考虑 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;
}