ST03Day2 平衡树

正题

      主要还是用到splay,treap也要会,多打几遍,可以用来可持久化。

      板子就不说了,回去多打几遍吧(flag1

      AVL树和红黑树也不多说了。

      splay的复杂度分析可以回去搞一搞,还挺有意思的,但是老师上课也是直接过(flag2

      [bzoj1014]火星人prefix

      回去写一写,就是用平衡树来维护区间的hash值,然后在外层套一个二分就可以。(flag3

      Gty的游戏

      没有加石子我们可以考虑分奇偶层讨论,然后用dfs来维护子树异或值就可以了,有石子怎么办,在dfs序上找到右边第一个dep比自己小的节点,记它为nex,后面无论什么时刻,[dfn[i],dfn[nex])都是子树。nex实际上就是平衡树加进来的时候的后继。(flag4

       TJOI2013 最长上升子序列

      因为保证数字递增,所以只要知道前面哪个的f值最大即可,带插入强制在线只能平衡树。

      BZOJ2658 小蓝的好友

      因为题目保证黑格子随机,所以我们可以知道对于一行,我们如果处理了一个点往上走最多能走多少,那么就是一个随机的序列,这等价于treap中的fix优先值,然后建立一个treap,算答案可以直接对于每一个节点用组合数算,如果从上往下扫,要支持的操作就是全局+1,部分位置变为0,那么把那些位置分离出来,然后改成0合并回去就可以,具体细节要打一打才知道(flag5

      平衡树的启发式合并

       O(n log n)

       Unknown

      题面:给定一颗树,求任取一个节点x和其子树内一个叶子节点y,y到x路径的最长不下降子序列的长度的最大值。

      输出对于每一个x的答案?

      自下而上用平衡树启发式合并按权值为关键字维护Dp值的前缀最大值即可,这个没写过也要写一写(flag6

      经典树分治题

      给定一棵树,求有多少对点的路径不超过K。

      这题给两个思路,都挺显然的:

      1.考虑点分治,那么只需要在每一层的重心维护一个树状数组就可以。

      2.考虑直接在原树上用splay启发式合并,每次合并查询一下,由于查询不支持平衡树性质,所以也是两个log,但常数较大,为什么查询不支持平衡树性质要放在讨论splay复杂度的时候去考虑(flag7

      逆序对

      看清楚题,就可以知道只需要交换两个部分,因为是排列,所以将一个元素移到最后面的代价是n+1-2pi,用splay来支持交换,维护前缀最小值即可。

      Hitchhiking in the Baltic States

      考虑求LIS的另外一种形式,记录长度为j的LIS最后一位最小是多少,因为问的是严格递增,那么我们可以将第i位-i然后再离散化。

      那么明显可以得到f[i][j]->f[i+1][j] || max(f[i][j]+1,l_{i+1})\to f[i+1][j+1](f[i][j]<r_{i+1})

      以j为关键字,f为权值建一棵平衡树,每次修改相当于将权值在[l_{i+1},r_{i+1}-1]之间的f右移一位,再+1,右移可以考虑删除该区间最右边的元素,再在区间左边插入一个与原来最左边元素相同的元素,再特殊处理一下最右边的后继节点。

      T-shirts

      

      以剩余钱数为关键字建一棵平衡树,按品质降序,价格升序,显然贪心买是没有问题的,考虑当前要花x元,那么将[x,2x)拿出来就可以了-x之后插回去,然后将[x,2x)打上标记-x就可以了,这样每个点只会被缩log次,所以时间复杂度就是两个log。

      NOI2007 货币兑换

      这题咕咕咕,回去一定补(flag8

      merge

      

      可以把一个前缀最大值看成是一个关键点,一个关键点到下一个关键点的前一个点称为一段,那么,一个关键点一旦被加进去了,后面的一段也一定被加进去了,对于段按照位置来建平衡树,然后对于m所在的区间,那么就暴力往后做一遍前缀最大值,拆成若干个段,在平衡树上二分找到拆出来的段应在的位置,就可以了,由于最多被拆成n个元素,每次加和删只跟拆出来的块数有关,所以答案就是n log n

      HNOI2017 单旋

      老师没讲,回去做做(flag9

剩下的就是替罪羊树和KDT,这两个东西回去会写模板就行,多练(flag10

根据原作 https://pan.quark.cn/s/459657bcfd45 的源码改编 Classic-ML-Methods-Algo 引言 建立这个项目,是为了梳理和总结传统机器学习(Machine Learning)方法(methods)或者算法(algo),和各位同仁相互学习交流. 现在的深度学习本质上来自于传统的神经网络模型,很大程度上是传统机器学习的延续,同时也在不少时候需要结合传统方法来实现. 任何机器学习方法基本的流程结构都是通用的;使用的评价方法也基本通用;使用的一些数学知识也是通用的. 本文在梳理传统机器学习方法算法的同时也会顺便补充这些流程,数学上的知识以供参考. 机器学习 机器学习是人工智能(Artificial Intelligence)的一个分支,也是实现人工智能最重要的手段.区别于传统的基于规则(rule-based)的算法,机器学习可以从数据中获取知识,从而实现规定的任务[Ian Goodfellow and Yoshua Bengio and Aaron Courville的Deep Learning].这些知识可以分为四种: 总结(summarization) 预测(prediction) 估计(estimation) 假想验证(hypothesis testing) 机器学习主要关心的是预测[Varian在Big Data : New Tricks for Econometrics],预测的可以是连续性的输出变量,分类,聚类或者物品之间的有趣关联. 机器学习分类 根据数据配置(setting,是否有标签,可以是连续的也可以是离散的)和任务目标,我们可以将机器学习方法分为四种: 无监督(unsupervised) 训练数据没有给定...
问题2: 建模思路:针对问题2,天气仅当天可知,需当天动态决策,结合场景法覆盖“天气不确定性”的建模思路,核心围绕随机规划( Stochastic Programming )适配动态天气场景的逻辑展开,,将“动态决策”转化为“多场景下的滚动优化”。 建模准备: 与第一问的衔接 ●复用基础模型:第一问的“确定性MIP模型”(路径、资源、采矿/采购逻辑)直接作为单场景求解器。 ●扩展动态逻辑:通过场景“并行求解多天气可能”,再聚合结果得到动态环境下的鲁棒策略。 关键说明 ●场景数量:需平衡“计算量”与“场景代表性”,用蒙特卡洛抽样保证覆盖主要天气风险。 ●动态反馈:实际游戏中,当日天气已知后,可“截断场景,仅保留已发生天气的分支”滚动优化(更贴近真实决策)。 建模过程:场景法适配建模步骤 1.场景生成:覆盖天气不确定性 ●定义天气维度:天气为“晴朗、高温、 沙暴”,设总天数为T,每日天气是随机变量[数学公式] ●生成场景序列:用蒙特卡洛模拟生成N条天气场景序列[数学公式],每条[数学公式]代表一种“从第1天到第T天’的天气可能。 2.动态决策与模型复用 ●决策分:将每日行动(移动/停留/采矿/采购)拆解为“阶段决策”,第t天的决策 [数学公式]依赖: 当日天气[数学公式] (已知时动态调整); 前t- 1天的资源/资金状态(路径依赖)。 ●复用MIP框架:对每条场景 [数学公式],将“动态天气”代入第一问的确定性MIP模型(资源约束、移动规则、采矿/采购逻辑不变),求解该场景下的最优路径与决策序列 [数学公式] 3.策略聚合:鲁棒性与期望优化 保守策略:筛选所有场景中“资源永不崩盘、必达终点”的行动集合,保证极端天气下存活。 目标:最大化“最坏场景下的剩余资金” ●期望最优:计算各场景决策的平均最终资金,目标:选期望最大的方案。 ●鲁棒优化:找“最坏天气场景(如连续沙暴)”中表现最好的决策(Min-Max思想)。 4.模型数学表达(简化版) 以“资源约束+场景适配”为核心,构建随机规划模型: [数学公式]St [数学公式] E[[数学公式]]:对N个场景的期望; [数学公式]:场景n第t天的资源(水/食物); C[数学公式]:天气[数学公式]下行动[数学公式]的资源消耗; S[数学公式]:行动[数学公式]的资源补充(如村庄采购、矿山不采矿时无补充)。 目标函数: ① 期望最优(最大化平均剩余资金)(用问题1的MIP模型求解单场景最优): [数学公式] ② 鲁棒最优(最坏场景下资金最大): [数学公式]import numpy as np import pandas as pd from tabulate import tabulate import random import math from collections import defaultdict, deque import heapq import networkx as nx import matplotlib.pyplot as plt from matplotlib.patches import Patch # 基础参数设置 WEATHER_TYPES = ['晴朗', '高温'] # 第三关已知不会出现沙暴天气 WEATHER_PROBS = [0.5, 0.5] # 假设晴朗和高温概率相等 SCENARIO_COUNT = 50 # 场景数量 MAX_DAYS = 10 # 最大天数 START_MONEY = 10000 # 初始资金 MAX_WEIGHT = 1200 # 最大负重 WATER_PRICE = 5 # 水单价 FOOD_PRICE = 10 # 食物单价 WATER_WEIGHT = 3 # 水的单位重量 FOOD_WEIGHT = 2 # 食物单位重量 # 资源消耗量(按天气类型) WEATHER_CONSUMPTION = { '晴朗': {'water': 3, 'food': 4}, '高温': {'water': 9, 'food': 9} } # 地图参数 n = 13 adj_matrix = [[0 for _ in range(n+1)] for _ in range(n+1)] edges = [(1,2),(1,4),(1,5),(2,3),(2,4),(3,4),(3,8),(3,9),(4,5),(4,6),(4,7), (5,6),(6,7),(6,12),(6,13),(7,12),(7,11),(8,9),(9,10),(9,11), (10,11),(10,13),(11,12),(11,13),(12,13)] for u, v in edges: adj_matrix[u][v] = 1 adj_matrix[v][u] = 1 # 节点类型定义 NODE_TYPES = { 'NORMAL': 0, 'START': 1, # 起点 'MINE': 2, # 矿山 'VILLAGE': 3, # 村庄 'END': 4 # 终点 } # 设置特殊节点 node_types = [NODE_TYPES['NORMAL']] * (n + 1) node_types[1] = NODE_TYPES['START'] # 起点 node_types[9] = NODE_TYPES['MINE'] # 矿山 node_types[13] = NODE_TYPES['END'] # 终点 # 生成天气场景 def generate_weather_scenarios(days, count): """生成指定天数和数量的天气场景""" scenarios = [] for _ in range(count): scenario = random.choices(WEATHER_TYPES, weights=WEATHER_PROBS, k=days) scenarios.append(scenario) return scenarios # 创建基础图 def create_graph(): G = nx.Graph() for u, v in edges: G.add_edge(u, v) return G # 确定性MIP模型求解器(改进版) def solve_deterministic_model(G, weather_sequence, start=1, end=13): """求解确定性模型,返回最优路径和收益""" best_result = None # 使用队列进行BFS搜索 # 状态包括: (当前位置, 路径, 水, 食物, 资金, 天数, 采矿天数) queue = deque() visited = set() # 初始购买策略(在起点购买资源) max_water = min(MAX_WEIGHT // WATER_WEIGHT, START_MONEY // WATER_PRICE) max_food = min(MAX_WEIGHT // FOOD_WEIGHT, START_MONEY // FOOD_PRICE) # 尝试不同的初始购买组合 for water in range(0, max_water + 1, 5): # 步长为5以减少状态空间 for food in range(0, max_food + 1, 5): cost = water * WATER_PRICE + food * FOOD_PRICE if cost > START_MONEY: continue if water * WATER_WEIGHT + food * FOOD_WEIGHT > MAX_WEIGHT: continue money = START_MONEY - cost state_key = (start, 0, water, food, money) if state_key in visited: continue visited.add(state_key) queue.append((start, [start], water, food, money, 0, [])) while queue: current, path, water, food, money, day, mining_days = queue.popleft() # 如果到达终点 if current == end: # 终点退回剩余资源 refund = water * WATER_PRICE / 2 + food * FOOD_PRICE / 2 money += refund if best_result is None or money > best_result[2]: best_result = (path, mining_days, money) continue # 如果超过最大天数 if day >= len(weather_sequence): continue # 获取当前天气 weather = weather_sequence[day] # 计算基础消耗 water_consumption = WEATHER_CONSUMPTION[weather]['water'] food_consumption = WEATHER_CONSUMPTION[weather]['food'] # 尝试所有可能的行动 # 1. 在当前区域停留(仅在村庄或矿山有意义) if node_types[current] == NODE_TYPES['VILLAGE'] or node_types[current] == NODE_TYPES['MINE']: if water >= water_consumption and food >= food_consumption: new_water = water - water_consumption new_food = food - food_consumption # 在村庄停留时可以购买资源 if node_types[current] == NODE_TYPES['VILLAGE']: # 计算最大可购买量 max_water_buy = min((MAX_WEIGHT - (new_food * FOOD_WEIGHT)) // WATER_WEIGHT, money // WATER_PRICE) max_food_buy = min((MAX_WEIGHT - (new_water * WATER_WEIGHT)) // FOOD_WEIGHT, money // FOOD_PRICE) # 尝试购买资源(步长为5) for buy_water in range(0, max_water_buy + 1, 5): for buy_food in range(0, max_food_buy + 1, 5): cost = buy_water * WATER_PRICE + buy_food * FOOD_PRICE if money < cost: continue new_water2 = new_water + buy_water new_food2 = new_food + buy_food new_money = money - cost # 检查负重 weight = new_water2 * WATER_WEIGHT + new_food2 * FOOD_WEIGHT if weight > MAX_WEIGHT: continue # 创建新状态 state_key = (current, day+1, new_water2, new_food2, new_money) if state_key not in visited: visited.add(state_key) new_path = path.copy() # 停留不改变路径 queue.append((current, new_path, new_water2, new_food2, new_money, day+1, mining_days.copy())) else: # 矿山停留但不采矿 state_key = (current, day+1, new_water, new_food, money) if state_key not in visited: visited.add(state_key) new_path = path.copy() # 停留不改变路径 queue.append((current, new_path, new_water, new_food, money, day+1, mining_days.copy())) # 2. 移动到相邻区域 for neighbor in G.neighbors(current): # 检查资源是否足够一天消耗 if water >= water_consumption and food >= food_consumption: new_water = water - water_consumption new_food = food - food_consumption new_money = money # 在村庄购买资源(到达时立即购买) if node_types[neighbor] == NODE_TYPES['VILLAGE']: # 计算最大可购买量 max_water_buy = min((MAX_WEIGHT - (new_food * FOOD_WEIGHT)) // WATER_WEIGHT, new_money // WATER_PRICE) max_food_buy = min((MAX_WEIGHT - (new_water * WATER_WEIGHT)) // FOOD_WEIGHT, new_money // FOOD_PRICE) # 尝试购买资源(步长为5) for buy_water in range(0, max_water_buy + 1, 5): for buy_food in range(0, max_food_buy + 1, 5): cost = buy_water * WATER_PRICE + buy_food * FOOD_PRICE if new_money < cost: continue new_water2 = new_water + buy_water new_food2 = new_food + buy_food new_money2 = new_money - cost # 检查负重 weight = new_water2 * WATER_WEIGHT + new_food2 * FOOD_WEIGHT if weight > MAX_WEIGHT: continue # 创建新状态 state_key = (neighbor, day+1, new_water2, new_food2, new_money2) if state_key not in visited: visited.add(state_key) new_path = path + [neighbor] new_mining_days = mining_days.copy() queue.append((neighbor, new_path, new_water2, new_food2, new_money2, day+1, new_mining_days)) else: # 非村庄节点 state_key = (neighbor, day+1, new_water, new_food, new_money) if state_key not in visited: visited.add(state_key) new_path = path + [neighbor] new_mining_days = mining_days.copy() queue.append((neighbor, new_path, new_water, new_food, new_money, day+1, new_mining_days)) # 3. 在矿山采矿(如果当前在矿山) if node_types[current] == NODE_TYPES['MINE'] and day < len(weather_sequence) - 1: # 采矿需要两天时间,消耗两天的资源 total_water = water_consumption * 2 total_food = food_consumption * 2 if water >= total_water and food >= total_food: new_water = water - total_water new_food = food - total_food new_money = money + 1000 # 采矿收益 # 标记采矿天数 new_mining_days = mining_days.copy() new_mining_days.append(day + 1) # 记录第二天采矿 # 创建新状态 state_key = (current, day+2, new_water, new_food, new_money) if state_key not in visited: visited.add(state_key) new_path = path.copy() # 采矿不改变位置 queue.append((current, new_path, new_water, new_food, new_money, day+2, new_mining_days)) return best_result if best_result else (None, None, 0) # 场景评估函数 def evaluate_scenarios(G, scenarios, start=1, end=13): """评估所有场景,返回每个场景的最优解""" scenario_results = [] for i, scenario in enumerate(scenarios): print(f"处理场景 {i+1}/{len(scenarios)}") # 求解该场景的最优策略 path, mining_days, final_money = solve_deterministic_model(G, scenario, start, end) # 计算总行程天数 days = len(scenario) # 计算路径长度 path_length = len(path) - 1 if path else float('inf') # 记录结果 - 完整输出天气序列和路径 scenario_results.append({ 'scenario_id': i+1, 'weather_sequence': '→'.join(scenario), # 完整天气序列 'path': '→'.join(map(str, path)) if path else '无路径', # 完整路径 'path_length': path_length, 'mining_days': '→'.join(map(str, mining_days)) if mining_days else '无采矿', 'total_days': days, 'final_money': final_money }) return scenario_results # 策略聚合函数 def aggregate_strategies(results): """聚合场景结果,生成不同策略""" # 转换为DataFrame df = pd.DataFrame(results) # 过滤无效结果 valid_df = df[df['final_money'] > 0] if len(valid_df) == 0: return None # 1. 期望最优策略(平均资金最高) expected_optimal = valid_df.loc[valid_df['final_money'].idxmax()] # 2. 最坏情况最优(最差场景中表现最好) worst_case_optimal = valid_df.sort_values('final_money').iloc[0] # 3. 鲁棒策略(最差20%场景中表现最好的) robust_df = valid_df.sort_values('final_money').iloc[:max(1, len(valid_df)//5)] robust_strategy = robust_df.sort_values('final_money').iloc[-1] return { 'expected_optimal': expected_optimal, 'worst_case_optimal': worst_case_optimal, 'robust_strategy': robust_strategy } # 结果展示函数 def display_results(results, strategies): """显示结果表格""" # 转换为DataFrame df = pd.DataFrame(results) # 打印统计信息 print("\n=== 场景统计信息 ===") valid_count = len(df[df['final_money'] > 0]) print(f"有效场景数: {valid_count}/{len(df)}") if valid_count > 0: valid_df = df[df['final_money'] > 0] print(f"平均剩余资金: {valid_df['final_money'].mean():.2f} 元") print(f"最低剩余资金: {valid_df['final_money'].min()} 元") print(f"最高剩余资金: {valid_df['final_money'].max()} 元") print(f"平均路径长度: {valid_df['path_length'].mean():.2f} 步") # 打印策略对比 if strategies: print("\n=== 策略对比 ===") strategy_table = [] for strategy_name, row in strategies.items(): strategy_table.append([ strategy_name, row['final_money'], row['path'], row['mining_days'], row['total_days'] ]) print(tabulate(strategy_table, headers=[ '策略类型', '剩余资金(元)', '路径', '采矿天数', '总天数' ], tablefmt='grid')) # 保存为Excel output_file = "第三关结果.xlsx" df.to_excel(output_file, index=False) print(f"\n所有场景结果已保存至 '{output_file}'") # 主函数 def main(): print("=== 第三关建模开始 ===") # 创建图 G = create_graph() # 生成天气场景 print(f"生成 {SCENARIO_COUNT} 个天气场景...") scenarios = generate_weather_scenarios(MAX_DAYS, SCENARIO_COUNT) # 评估所有场景 print("评估所有场景...") scenario_results = evaluate_scenarios(G, scenarios) # 聚合策略 print("聚合策略...") strategies = aggregate_strategies(scenario_results) # 显示结果 display_results(scenario_results, strategies) print("\n=== 建模完成 ===") if __name__ == "__main__": main()
07-26
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值