当前位置: 首页 > news >正文

2026“钉耙编程”中国大学生算法设计春季联赛(7)1009思路分享(单调栈,倍增,分治/树链剖分,线段树上二分)

题意概述

一个长度为 \(n\) 的序列 \(a\),初始全 \(0\)

有一个长度为 \(m\) 的操作序列 \((r_i, v_i)\),每个操作表示将 \(a[1, r_i]\)\(v_i\)\(\max\)

给定 \(q\) 次询问,求依次执行 \([L_i, R_i]\) 里面的操作之后 \(a\) 的和。

特别地,设 \(p_i\) 表示左边第一个 \(v_j > v_i\) 的位置,那么有 \(\max_{j=p_i+1}^i r_j = r_i\)

输出所有询问答案的异或和。

多测,\(T=5,1 \le n, m, q \le 10^5,1 \le v_i \le 10^9\)

思路

根据特别约束,如果询问区间 \([L, R]\) 包含 \(i\),那么 \([p_i+1,i-1]\) 是没有贡献的。考虑连 \(p_i\)\(i\) 的边,建出来的图是个森林。求 \(p_i\) 的过程可以用单调栈实现。

每次查询所有需要考虑的点就是 \([L, R]\) 包含的最长链,记深度小的为链头。

不难发现,链中元素的 \(v\) 是递减的,于是链中能产生贡献的点就是 \(r_u > curR\) 的点,这样会产生 \(v_u \times (r_u - curR)\) 的贡献。而通过维护 \(r\) 的最大值,这样的点是可以在链上二分找到的,那么先找链头,从链头开始跳即可。

可以倍增或者树剖实现,每个询问的链都是由若干个块拼接形成,每次跳跃都是找深度最小的 \(u\) 满足 \(r_u \gt curR\) ,倍增的做法就维护块的最大 \(r\),分治寻找;树剖的做法就用线段树维护最大 \(r\),在线段树上二分寻找。

期望时间复杂度应该是 \(\mathcal{O}(q\cdot \log^2 m)\) ,预处理部分取决于实现。

倍增代码

//author:kzssCCC#include <bits/stdc++.h>
using namespace std;
using ll = long long;const int INF = 1e9;void solve(){int n,m,q;cin >> n >> m >> q;vector<int> R(m+1);vector<ll> V(m+1);for (int i=1;i<=m;i++){cin >> R[i] >> V[i];}vector<int> p(m+1);stack<int> stk;for (int i=1;i<=m;i++){while (!stk.empty() && V[i]>=V[stk.top()]){stk.pop();}p[i] = stk.empty()?-1:stk.top();stk.push(i);}vector<vector<int>> dp(m+1,vector<int>(32,-1));vector<vector<int>> mxr(m+1,vector<int>(32,-INF));for (int i=1;i<=m;i++){if (p[i]!=-1){dp[i][0] = p[i];mxr[i][0] = R[i];		}}for (int k=1;k<32;k++){for (int i=1;i<=m;i++){if (dp[i][k-1]==-1) continue;dp[i][k] = dp[dp[i][k-1]][k-1];mxr[i][k] = max(mxr[i][k-1],mxr[dp[i][k-1]][k-1]);}}ll fres = 0;while (q--){int l,r;cin >> l >> r;vector<pair<int,int>> wait;int cur = r;for (int k=31;k>=0;k--){if (dp[cur][k]>=l){wait.emplace_back(cur,k);cur = dp[cur][k];}}reverse(wait.begin(),wait.end());ll res = V[cur]*R[cur];ll curR = R[cur];function<void(int,int)> cal = [&](int s,int k){if (mxr[s][k]<=curR) return;if (k==0){res += V[s]*(R[s]-curR);curR = R[s];return;}cal(dp[s][k-1],k-1);cal(s,k-1);};for (auto& [s,k]:wait){cal(s,k);}fres^=res;}cout << fres << '\n';
}int main(){ios::sync_with_stdio(false);cin.tie(0);int t;cin >> t;while (t--) solve();return 0;
}

树剖代码

//author:kzssCCC#include <bits/stdc++.h>
using namespace std;
using ll = long long;const int INF = 1e9;class node{
public:int mx = -INF;
};class segmentTree{
public:int n;vector<node> seg;segmentTree(int _n){n = _n;seg = vector<node>(4*n+1);}node merge(node p1,node p2){node temp;temp.mx = max(p1.mx,p2.mx);return temp;}void build(vector<ll>& a){build(1,1,n,a);}	void build(int rt,int l,int r,vector<ll>& a){if (l==r){seg[rt].mx = a[l];return;}	int mid = l+r >> 1;build(rt<<1,l,mid,a);build(rt<<1|1,mid+1,r,a);seg[rt] = merge(seg[rt<<1],seg[rt<<1|1]);			}void push_down(int rt,int l,int r){}void update(int pos,ll val){update(1,1,n,pos,val);}void update(int rt,int l,int r,int pos,ll val){if (l==r){//return;}		int mid = l+r >> 1;push_down(rt,l,r);if (pos<=mid){update(rt<<1,l,mid,pos,val);}else{update(rt<<1|1,mid+1,r,pos,val);}seg[rt] = merge(seg[rt<<1],seg[rt<<1|1]);}void update_range(int x,int y,ll val){update_range(1,1,n,x,y,val);}void update_range(int rt,int l,int r,int x,int y,ll val){if (r<x || l>y){return;}if (x<=l && y>=r){//return;}int mid = l+r >> 1;push_down(rt,l,r);update_range(rt<<1,l,mid,x,y,val);update_range(rt<<1|1,mid+1,r,x,y,val);seg[rt] = merge(seg[rt<<1],seg[rt<<1|1]);}node query(int pos){if (pos<1 || pos>n) return {};return query(1,1,n,pos);}node query(int rt,int l,int r,int pos){if (l==r){return seg[rt];}		int mid = l+r >> 1;push_down(rt,l,r);if (pos<=mid){return query(rt<<1,l,mid,pos);}else{return query(rt<<1|1,mid+1,r,pos);}}node query_range(int l,int r){if (l<1 || l>n || r<1 || r>n || l>r) return {};return query_range(1,1,n,l,r);}node query_range(int rt,int l,int r,int x,int y){if (r<x || l>y){return {};}if (x<=l && y>=r){return seg[rt];}int mid = l+r >> 1;push_down(rt,l,r);return merge(query_range(rt<<1,l,mid,x,y),query_range(rt<<1|1,mid+1,r,x,y));}bool is_valid(int rt,int l,int r,int x,int y,ll val){if (r<x || l>y) return false;if (seg[rt].mx>val){return true;}return false;}int first_valid(int l,int r,ll val){if (l>r) return -1;return first_valid(1,1,n,l,r,val);}int first_valid(int rt,int l,int r,int x,int y,ll val){if (!is_valid(rt,l,r,x,y,val)) return -1;if (l==r){return l;}int mid = l+r >> 1;push_down(rt,l,r);int res = first_valid(rt<<1,l,mid,x,y,val);if (res!=-1){return res;}return first_valid(rt<<1|1,mid+1,r,x,y,val);}int last_valid(int l,int r,ll val){if (l>r) return -1;return last_valid(1,1,n,l,r,val);		}int last_valid(int rt,int l,int r,int x,int y,ll val){if (!is_valid(rt,l,r,x,y,val)) return -1;if (l==r){return l;}int mid = l+r >> 1;push_down(rt,l,r);int res = last_valid(rt<<1|1,mid+1,r,x,y,val);if (res!=-1){return res;}return last_valid(rt<<1,l,mid,x,y,val);}	
};void solve(){int n,m,q;cin >> n >> m >> q;vector<int> R(m+1);vector<ll> V(m+1);for (int i=1;i<=m;i++){cin >> R[i] >> V[i];}vector<int> p(m+2,-1);stack<int> stk;vector<vector<int>> adj(m+2);for (int i=1;i<=m;i++){while (!stk.empty() && V[i]>=V[stk.top()]){stk.pop();}p[i] = stk.empty()?-1:stk.top();stk.push(i);}for (int i=1;i<=m;i++){if (p[i]!=-1){adj[p[i]].push_back(i);}}vector<int> sz(m+1),depth(m+1),dfn(m+1),top(m+1),ref(m+1),son(m+1);function<void(int)> dfs = [&](int u){sz[u] = 1;int pos = -1;int mx = 0;for (auto& v:adj[u]){depth[v] = depth[u]+1;dfs(v);sz[u] += sz[v];if (sz[v]>mx){mx = sz[v];pos = v;}	}son[u] = pos;};for (int i=1;i<=m;i++){if (p[i]==-1){dfs(i);}}int timer = 1;function<void(int,int)> dfs2 = [&](int u,int head){dfn[u] = timer++;ref[dfn[u]] = u;top[u] = head;if (son[u]!=-1){dfs2(son[u],head);}		for (auto& v:adj[u]){if (v!=son[u]){dfs2(v,v);}}};for (int i=1;i<=m;i++){if (p[i]==-1){dfs2(i,i);}}segmentTree sg(m);vector<ll> b(m+1);for (int i=1;i<=m;i++){b[dfn[i]] = R[i];		}sg.build(b);ll fres = 0;while (q--){int l,r;cin >> l >> r;vector<int> wait;int cur = r;while (1){wait.push_back(cur);if (top[cur]>l){if (p[top[cur]]==-1 || p[top[cur]]<l){cur = top[cur];break;}cur = p[top[cur]];}else{int left = dfn[top[cur]];int right = dfn[cur];while (left<=right){int mid = left+right>>1;if (ref[mid]>=l){right = mid-1;}else{left = mid+1;}}cur = ref[left];break;}}int curR = R[cur];ll res = V[cur]*R[cur];reverse(wait.begin(),wait.end());for (auto& v:wait){while (1){int t = depth[cur]>depth[top[v]]?cur:top[v];int left = dfn[t];int right = dfn[v];int temp = sg.first_valid(left,right,curR);if (temp!=-1){int u = ref[temp];res += V[u]*(R[u]-curR);curR = R[u];cur = u;		}	else break;	}}			fres^=res;}	cout << fres << '\n';
}int main(){ios::sync_with_stdio(false);cin.tie(0);int t;cin >> t;while (t--) solve();return 0;
}
http://www.jsqmd.com/news/809815/

相关文章:

  • 【Layer Normalization论文阅读】:Transformer背后的归一化神器,从原理到代码实现
  • Gemini Pixel专属功能失效终极排查:覆盖12类系统冲突场景,含Android 15 Beta 3已知兼容性黑洞
  • 用Wireshark抓包实战:手把手教你解析USB键盘的端点描述符(附完整数据包分析)
  • 为什么数据科学家都爱用Spyder?这6个独特优势让你告别Python开发烦恼! [特殊字符]
  • 厂家直供更省心!2026浙江润鑫汽车轴重仪,48小时快速发货 - 品牌速递
  • 武汉市一豪卷帘门:专业的武汉车库门定制哪个厂家好 - LYL仔仔
  • 2026年酸逆流清洗系统哪家好?3万起国产替代进口解决方案 - 品牌推荐大师1
  • 用python自己回测股票策略
  • 基于PARA方法与Obsidian+Git构建个人知识管理系统的实践指南
  • stm32结合多模型api为智能硬件提供灵活的内容生成方案
  • MacBook M芯片用户看过来:最新macOS Sonoma/Ventura安装CH340驱动避坑指南
  • JAVA源码单商户PC源码小程序公众号APP源码的后端代码示例
  • 2026年亲测:从85%降到10%,保姆级论文降AI率去AI痕迹教程 - 降AI实验室
  • 如何构建完整的下一代测序实验室信息管理系统:MISO开源LIMS深度解析
  • 2026年天津洛阳柴火鸡汤加盟与土鸡汤馆选址完全指南|玖味时光楠溪王捌鸡官方联系电话 - 企业名录优选推荐
  • 面试官追问AUC和F1-Score区别?从推荐系统实战案例看指标选择与陷阱
  • 2026年青岛企业全场景营销与AI精准获客完全指南:从短视频代运营到GEO推广的降本增效闭环 - 年度推荐企业名录
  • 白话解读DSI3:从单线通信到多设备管理的核心机制
  • 如何打造工业级STM32温控系统:从零到精密的实战指南
  • 福州港文机械设备租赁:福州叉车租赁哪家好 - LYL仔仔
  • 告别数据错乱!STM32H743串口DMA接收的Cache一致性终极处理方案
  • 鞍山黄金回收公司选择指南 拆解专业回收技术细节 - 奔跑123
  • 别再只用外部中断了!STM32F4 HAL库驱动EC11编码器的三种实用方法(附代码对比)
  • Codeforces Round 1054 (Div. 3) E题
  • 2026年开封洛阳柴火鸡特色餐饮深度横评与选购指南 - 企业名录优选推荐
  • 2026年贵州柴火鸡特色餐饮选购指南:楠溪王捌鸡与行业竞品深度横评 - 企业名录优选推荐
  • 雨量监测站:实现降雨量实时精准计量
  • 张家口黄金回收哪家靠谱?金裕恒 / 盛誉轩 / 金成瑞连锁实测,无套路 - 润富黄金珠宝行
  • 在自动化Agent工作流中集成Taotoken实现多模型决策与调用
  • JPEGView:Windows上最轻量高效的图像查看与编辑解决方案