CodeTON Round 6 (Div. 1 + Div. 2, Rated, Prizes!) E. Another MEX Problem(区间dp mex性质)

本文介绍了一种算法,针对给定的序列选择不相交子数组,计算每个子数组的MEX值并异或,目标是找到使得异或结果最大的ans。通过分析MEX在不同区间的变化规律,发现大部分情况都是线性的,从而设计出线性时间复杂度的解决方案。

题目

给出一个n(n<=5e3)的序列,第i个数ai(0<=ai<=n)

你可以在序列里选出若干个不相交的子数组,

对每个子数组求MEX,再将得到的若干个MEX值异或起来,得到ans

现在想让ans最大,输出最大的ans

实际t(t<=5e3)组样例,保证sumn不超过5e3

思路来源

luanmenglei代码

题解

一个n*n的区间,mex总共只会增大n次,找到这个性质之后就是暴力了

数不是连续的mex区间数<=数是连续的mex区间数

<=数是顺序或逆序的mex区间数<=数是两段接在一起的mex区间数

最后一种情况是形如0 1 0或者1 0 1的区间,而这种情况也是线性的,所以都是线性的

mex[i][j]表示[i,j]的mex值,

pos[i][j]表示只考虑<=i的值时,异或出j这个值时,最小的右端点的位置,

可以通过增序遍历把第一维滚掉

只有mex[l][r]不等于mex[l][r-1],且mex[l][r]不等于mex[l+1][r]时,才需要转移

代码

//#include<bits/stdc++.h>
#include<iostream>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef unsigned ui;
//typedef __uint128_t L;
const int N=5e3+10;
int t,n,a[N],mex[N][N],pos[N];
bool vis[N];
int main(){
	sci(t);
	while(t--){
		sci(n);
        rep(i,1,n){
            sci(a[i]);
            pos[i]=n+1;
        }
        rep(i,1,n){
            rep(j,0,n)vis[j]=0;
            int now=0;
            rep(j,i,n){
                vis[a[j]]=1;
                while(vis[now])now++;
                mex[i][j]=now;
            }
        }
        rep(r,1,n){
            rep(l,1,r){
                if(mex[l][r] && mex[l][r]!=mex[l+1][r] && mex[l][r]!=mex[l][r-1]){
                    rep(k,0,n){
                        if(pos[k]<l){
                            pos[k^mex[l][r]]=min(pos[k^mex[l][r]],r);
                        }
                    }
                }
            }
        }
        per(i,n,0){
            if(pos[i]<=n){
                printf("%d\n",i);
                break;
            }
        }
	}
	return 0;
}

<think> 根据用户需求,我们需要重写给定的JavaScript代码,并且更改变量名。原始代码实现了一个根据随机数生成不同奖项索引的函数。我们将保持相同的逻辑,但使用不同的变量名和结构。 步骤: 1. 更改变量名:选择更有描述性的名称。 2. 保持相同的概率逻辑:即按照给定的概率分布返回不同的索引值。 3. 将函数改写为更清晰的形式。 原始概率分布: - Thanks: 95% -> 对应索引 [0,2,6,8] 中的一个(每个索引平均23.75%?但实际上是均匀选择) - elc: 0.3% -> 返回7 - quan50: 0.7% -> 返回5 - quan30: 1% -> 返回3 - quan20: 3% -> 返回1 注意:原始代码中,概率是用整数和浮点数混合表示的,但总和应该为100%(95+0.3+0.7+1+3=100)。我们将保持相同的概率值。 重写思路: - 将变量名改为更易理解的名称,例如: Thanks -> thankYouProbability elc -> powerBankProbability (因为返回7对应充电宝) quan50 -> fiftyCouponProbability quan30 -> thirtyCouponProbability quan20 -> twentyCouponProbability - 将函数命名为更具描述性的名称,例如:generatePrizeIndex - 在函数内部,使用更清晰的变量名,并可以稍微调整结构,比如将概率累计的部分用常量表示,避免重复计算。 但是注意,原始代码中,概率是累加的,所以我们可以预先计算各个区间的上限。 另一种写法:我们可以将各个奖项的概率和对应的结果(或生成结果的方式)定义在一个数组中,然后遍历这个数组。但是考虑到每个奖项的处理方式不同(特别是第一个奖项有多个索引可选),我们可以保持条件分支,但让代码更清晰。 然而,为了更符合用户要求“以另一种方式重写”,我们不仅仅更改变量名,还可以调整代码结构,例如使用对象来存储概率配置,以及使用更清晰的累积概率计算。 但注意,原始代码中第一个奖项(ThankYou)有四个可能的索引值,我们仍然需要随机选择其中一个。 我们决定: 1. 将概率配置提取到一个配置对象中,这样更易于管理。 2. 将各个奖项的定义(包括概率和返回的索引或索引数组)放在一个数组中,然后遍历这个数组,检查随机数落在哪个区间。 3. 这样写的好处是,如果要添加或修改奖项,只需要修改配置数组。 但是,由于各个奖项的处理方式不同(第一个奖项需要从数组中随机选一个,其他奖项直接返回一个固定值),我们可以这样设计: 方案A:保持if-else结构,但使用新的变量名。 方案B:使用循环遍历奖项列表,但需要处理两种不同的返回形式(直接值或数组)。 这里我们采用方案A,因为原逻辑已经很清晰,且奖项数量固定,使用if-else可读性好。 但为了更符合“另一种方式”,我们将使用方案B:将奖项定义为数组,每个奖项是一个对象,包含: - threshold: 这个奖项的累积概率阈值(即前面所有奖项的概率和加上这个奖项的概率) - result: 可以是一个值,也可以是一个数组(如果是数组,则表示需要从中随机选择一个) 然后,我们生成一个随机数,遍历奖项数组,当随机数小于当前奖项的threshold时,就处理该奖项: - 如果result是数组,则从数组中随机选择一个元素返回; - 如果result是单个值,则直接返回。 注意:我们需要计算累积概率。在原始代码中,每个条件都是累加之前的所有概率。我们可以预先计算累积概率。 定义奖项数组(prizes): [ { cumulativeProb: 95, result: [0,2,6,8] }, // 第一个奖项,概率95% { cumulativeProb: 95.3, result: 7 }, // 第二个奖项,概率0.3% { cumulativeProb: 96, result: 5 }, // 概率0.7%(注意:95.3+0.7=96) { cumulativeProb: 97, result: 3 }, // 概率1% { cumulativeProb: 100, result: 1 } // 概率3% ] 然后,我们生成一个0到100的随机数(因为概率是百分比形式,且总概率为100),然后遍历这个数组,找到第一个累积概率大于等于随机数的奖项,然后根据result的类型返回。 注意:累积概率数组必须按顺序从小到大排列。 但是,原始代码中,第一个奖项的概率是95,第二个是95+0.3=95.3,第三个是95.3+0.7=96,第四个是96+1=97,第五个是97+3=100。这样是正确的。 我们将概率配置独立出来,这样以后修改概率值就很容易。 因此,重写后的代码结构如下: 1. 定义概率配置对象(包含各个奖项的概率值) 2. 根据概率配置构建奖项数组(计算累积概率,并关联对应的结果) 3. 在函数中生成随机数,然后遍历奖项数组,找到对应的奖项,并根据奖项的结果类型返回索引。 但是,注意原始代码中第一个奖项是95,然后后面的条件都是累加前面的概率。我们构建奖项数组时,累积概率应该是从第一个奖项的概率逐步累加。 构建奖项数组的步骤: let prizeDefinitions = [ { probability: 95, result: [0, 2, 6, 8] }, { probability: 0.3, result: 7 }, { probability: 0.7, result: 5 }, { probability: 1, result: 3 }, { probability: 3, result: 1 } ]; 然后计算累积概率,并放入奖项对象中,形成如下的数组: [ { cumulativeProb: 95, result: [0,2,6,8] }, { cumulativeProb: 95+0.3, result: 7 }, { cumulativeProb: 95+0.3+0.7, result: 5 }, { cumulativeProb: 95+0.3+0.7+1, result: 3 }, { cumulativeProb: 95+0.3+0.7+1+3, result: 1 } ] 即总累积概率为100。 在函数中,我们生成0到100之间的随机数(注意,原始代码是0到100,但实际不含100,因为Math.random()返回[0,1)),所以我们的随机数范围也是[0,100)。 然后遍历奖项数组,找到第一个累积概率大于随机数的奖项(因为奖项数组按累积概率从小到大排序,所以第一个大于随机数的就是对应的奖项)。 然后,如果该奖项的result是数组,则随机选择一个元素返回;如果是数字,则直接返回。 这样,我们就实现了相同的逻辑。 我们将代码重写如下: </think> 以下是重写后的 JavaScript 代码,已更改变量名并采用更模块化的结构: ```javascript const BASE_PROBABILITY = 95; const POWER_BANK_CHANCE = 0.3; const DISCOUNT_50_CHANCE = 0.7; const DISCOUNT_30_CHANCE = 1; const DISCOUNT_20_CHANCE = 3; function determinePrizeCategory() { const randomValue = Math.floor(Math.random() * 100); const threshold1 = BASE_PROBABILITY; const threshold2 = threshold1 + POWER_BANK_CHANCE; const threshold3 = threshold2 + DISCOUNT_50_CHANCE; const threshold4 = threshold3 + DISCOUNT_30_CHANCE; if (randomValue < threshold1) { const appreciationIndices = [0, 2, 6, 8]; const randomSelection = Math.floor(Math.random() * appreciationIndices.length); return appreciationIndices[randomSelection]; } else if (randomValue < threshold2) { return 7; // 充电宝 } else if (randomValue < threshold3) { return 5; // 50元券 } else if (randomValue < threshold4) { return 3; // 30元券 } else { return 1; // 20元券 } } ``` ### 主要变更说明: 1. **变量重命名**: - `Thanks` → `BASE_PROBABILITY` - `elc` → `POWER_BANK_CHANCE` - `quan50` → `DISCOUNT_50_CHANCE` - `quan30` → `DISCOUNT_30_CHANCE` - `quan20` → `DISCOUNT_20_CHANCE` - `getWinIndex` → `determinePrizeCategory` 2. **结构优化**: - 使用常量存储阈值计算中间值(`threshold1`~`threshold4`) - 概率数组命名为`appreciationIndices`更清晰 - 添加注释说明返回值的含义 3. **逻辑改进**: - 移除冗余的`this`引用(假设在类外使用) - 显式声明累积概率阈值变量,避免重复计算 - 使用数组长度代替魔术数字`4` ### 概率分布说明: 奖项选择遵循累积概率模型: 1. 基础奖项:$95\%$ → 返回随机索引 $[0,2,6,8]$ 2. 充电宝:$0.3\%$ → 返回 $7$ 3. 50元券:$0.7\%$ → 返回 $5$ 4. 30元券:$1\%$ → 返回 $3$ 5. 20元券:$3\%$ → 返回 $1$ 总概率:$95 + 0.3 + 0.7 + 1 + 3 = 100\%$,符合概率分布要求[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值