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

9.17支配对问题专题总结

概括

每次查询一定范围内的点组成的点对中的最优值,而通过一些分析去减少有用点对的数量,这样子的有用点对称为 支配对

T1

image

策略是将 \(a_i\) 相同的序列取出来单独考虑

固定 \(b_i<b_j,i<j\) 然后考虑能找出来一个序列 \(i,j1,j2,j3...\)

但是发现选 \(i,j2\) 不如选 \(j1,j2\) 更优,所以最终维护的支配对为 \((i,i+1)\) 即可

固定 \(b_i>b_j,i<j\) 时同理

考虑为什么这样的支配对能覆盖所有的查询

考虑一个区间询问 \((l,r)\) 最终取到的答案一定是区间次大值 q 和最大值 p,考虑我们在维护 q 的支配对时,一定能统计到 p,所以就做完了

类似的,支配对的统计也可以理解为所有的子区间询问造成贡献的对的并集

T2

image

题解

这次我们反着考虑

考虑一次区间查询 \((l,r)\) 能产生贡献的对有哪些

必然是当序列 \((l,r)\) 有序之后所有的 \((i,i+1)\) 都能产生贡献(也即值域上相邻的两项)

所以我们依旧固定 \(a_i<a_j,i<j\)\(a_i>a_j,i<j\) 两种情况进行考虑(这样分类能包含所有的对,稍加考虑一下就行)

单独考虑 \(a_i<a_j,i<j\) 情况:

固定住 \(i\) 找出来能产生贡献的 \(j\) 序列(就是满足 \(a_i>a_{ji},a_{ji}<a_{ji-1}\) 的序列)形如 \(i,j1,j2,j3...\)

然而发现如果 \(a_{j2}-a_i>a_{j1}-a_{j2}\) 那还不如选 \((j1,j2)\) 对优

所以选出来的对还要满足 \(a_{j2}-a_i<a_{j1}-a_{j2}\) 发现每次 \(a_{j}-a_i\) 相比至少减半,所以之多有 \(O(logn)\) 个对,做完了

实现

找支配对的子问题:

每次寻找权值在 \([L,R]\) 范围内的最小位置

开一颗权值线段树维护

统计答案的子问题:

有若干个对 \((l,r)\) 然后若干个询问 \((L,R)\),要求求满足 \(L<=l,r<=R\) 的对的贡献的最小值

经典二维数点问题,L 一维倒着排序,R 用树状数组维护前缀最小值

注意:

求区间最接近且不相等的两数之差的绝对值。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,inf=1e9+5,M=1e9;
int n,q,cnt,root;
int a[N],t[N],ans[N];
struct query{int r,i;
};
vector<int>pr[N];
vector<query>qu[N];
struct dot{int ls,rs,mn;
}tr[N*31];
struct tree1{int newnode(){tr[++cnt]={0,0,inf};return cnt;}void update(int k){tr[k].mn=min(tr[tr[k].ls].mn,tr[tr[k].rs].mn);}void add(int &k,int l,int r,int x,int y){if(!k)  k=newnode();if(l==r){tr[k].mn=y;return;}int mid=(l+r)>>1;if(x<=mid)  add(tr[k].ls,l,mid,x,y);else  add(tr[k].rs,mid+1,r,x,y);update(k);}int query(int k,int l,int r,int x,int y){if(!k)  return inf;if(x<=l&&r<=y){return tr[k].mn;}int mid=(l+r)>>1,res=inf;if(x<=mid)  res=min(res,query(tr[k].ls,l,mid,x,y));if(y>mid)  res=min(res,query(tr[k].rs,mid+1,r,x,y));return res;}void clear(){for(int i=1;i<=cnt;i++){tr[i]={0,0,0};}cnt=0;root=0;}
}tree1;
struct tree2{void init(){for(int i=1;i<=n;i++)  t[i]=inf;}int lowbit(int x){return x&(-x);}void add(int x,int k){for(;x<=n;x+=lowbit(x)){t[x]=min(t[x],k);}}int query(int x){int res=inf;for(;x;x-=lowbit(x)){res=min(res,t[x]);}return res;}
}tree2;
int main(){scanf("%d%d",&n,&q);for(int i=1;i<=n;i++){scanf("%d",&a[i]);}tr[0].mn=inf;//注意update时可能会更新0的答案for(int i=n;i>=1;i--){int l=a[i]+1,r=M;//一定要注意不相等的限制while(1){int num=tree1.query(root,1,M,l,r);if(num==inf)  break;pr[i].push_back(num);// printf("%d %d\n",i,num);r=(a[num]+a[i]+1)/2-1;if(r<=a[i])  break;}tree1.add(root,1,M,a[i],i);}tree1.clear();for(int i=n;i>=1;i--){int l=1,r=a[i]-1;while(1){int num=tree1.query(root,1,M,l,r);if(num==inf)  break;pr[i].push_back(num);// printf("%d %d\n",i,num);l=(a[num]+a[i])/2+1;if(l>=a[i])  break;}tree1.add(root,1,M,a[i],i);}for(int i=1;i<=q;i++){int l,r;scanf("%d%d",&l,&r);qu[l].push_back({r,i});}tree2.init();for(int i=n;i>=1;i--){for(int j:pr[i])  tree2.add(j,abs(a[i]-a[j]));for(auto j:qu[i])  ans[j.i]=tree2.query(j.r);}for(int i=1;i<=q;i++){printf("%d\n",ans[i]);}
}

T3

image

题解

看见树上距离直接上点分治

一次点分治内部的做法:

每个节点到根都会有一个距离

但是我们似乎无法判掉两个点在同一个子树中的贡献

但是何妨呢,两个点在同一个子树的距离一定大于两个点正常的距离,对答案没有影响

问题转化成了:

在一次点分治内部,每个点都有一个 \(b_i\) 然后需要使得 \(b_i+b_j\) 最小

转化为T1

做法

点分治不多说

然后先让 b 按照下标排序,然后问题转化成了:

\(b_i<=b_j,i<j\) 的第一个j,就是找i之后第一个大于i的数

单调栈维护

代码

T4

image

限制很神秘,但是肯定得通过一些分治什么的技巧简化问题

因为 x 的限制比较复杂,所以分治 y

现在变成了左下有一块,右上有一块,两块中的 x 要满足 \(L<=x_i,x_j<=R\)\(x_i<L<R<x_j\)

就是对于一组询问要么两个 x 都在里,要么都在外

一组不合法情况就是一个 x 在里一个在外

此时只需要调整一个即可

发现 \(w_i+w_j\) 最大的是找到两边的最大值相加是最优的

所以两边的最大值必然有一个在对中

所以维护所有包含最大值的对,一共有 \(O(N)\) 个,可以通过本题

T5

image

这种题就是猜,猜能拆成 \(i,j\) 分别求贡献的题目

然而事实上也能拆掉

考虑 \(\varphi (p_i\times p_j)\)\(\varphi (p_i)\times \varphi (p_j)\) 之间的关系

考虑到欧拉函数的公式 \(\varphi (x)=x\times \prod (1-\frac{1}{p})\)

然后 \(\varphi (p_i)\times \varphi (p_j)\) 他们就是多乘了 \(p_i\)\(p_j\) 的共同质因数产生的贡献

假设 \(p_i\)\(p_j\) 的值因子集合为 S 和 T,那么 \(\varphi (p_i)\times \varphi (p_j)\) 相当于直接将两个集合加起来,显然,要想求两个集合的并,多算了一个两个集合的交的贡献,所以乘上 \(\frac{\gcd(p_i,p_j)}{\varphi(\gcd(p_i,p_j))}\) 就能消掉交的贡献了

公式:

\[\varphi (p_i p_j)=\frac{\varphi (p_i) \varphi (p_j) \gcd(p_i,p_j)}{\varphi(\gcd(p_i,p_j))} \]

这样枚举 \(d=\gcd\) 然后将含有当前 \(d\) 的因数的所有元素取出,统计一遍答案,考虑如果 \(d\lt\gcd\) 会不会统计错误,发现它会多乘上几个 \((1-\frac{1}{p})\) 使得答案更小,不会造成影响

但是是 \(O(n^2)\),需要小小的优化

枚举每个数的因数,然后挂在因数所在的集合内,然后转化为T1

T6image

看到这种题还是枚举 \(d=\gcd\),分解每个数的因数,然后把数挂在上面,做法同上

然后在序列中找到 \(j-i\le k-j\),显然 \(j=i+1\) 最优,然后双指针找到最近的 \(k\)

复杂度分析一下发现有 \(O(n\sqrt n)\) 个支配对

问题变为有 \(O(n\sqrt n)\)\((l,r)\) 的对,有 \(O(q)\)\((L,R)\) 的查询 \(L\le l,r\le R\)

然后树状树组 \(O(n\sqrt n\log n+n\log n)\) 的,发现复杂度瓶颈在加入

所以考虑复杂度平衡

还是扫描线,但是我 \(O(1)\) 加入, \(O(\sqrt n)\) 分块查询,复杂度平衡了

http://www.wxhsa.cn/company.asp?id=6931

相关文章:

  • 记录知识
  • AT_agc058_b [AGC058B] Adjacent Chmax
  • Jenkins CVE-2018-1000600漏洞利用与SSRF攻击分析
  • NOIP 集训日记(学术)
  • linux中mysql如何远程连接
  • 详细介绍:Python:OpenCV 教程——从传统视觉到深度学习:YOLOv8 与 OpenCV DNN 模块协同实现工业缺陷检测
  • 深入解析:PYcharm——pyqt音乐播放器
  • Day02
  • 专题:Python实现贝叶斯线性回归与MCMC采样数据可视化分析2实例|附代码数据
  • 威联通NAS如何导入本地docker镜像
  • 【学习笔记】拉格朗日插值
  • 一种将离散化状态方程映射为并行多处理器计算机的方法
  • 基本数据类型题目
  • 一种基于动作指令交互的动态活体检测技术,提升人脸识别安全性
  • [系统] Windows 已有office版本和visio不兼容的解决方案
  • CF 2127F Hamed and AghaBalaSar
  • AT_agc055_b [AGC055B] ABC Supremacy
  • “Sequential Thinking MCP Server 和codex等AI工具本身任务拆解功能对比
  • 基于错误xsleak 悬空标记 运用css利用帧计数 -- Pure leak ASIS CTF 2025
  • 网易伏羲:当算法遇见社交,解码游戏世界的连接密码
  • 在 CentOS 7 上安装Nginx和配置http代理
  • 题解:P2624 [HNOI2008] 明明的烦恼
  • 在AI技术快速实现创想的时代,挖掘新需求成为核心竞争力——某知名DevOps学习平台需求洞察
  • Windows Powershell 获取版本version
  • XXL-JOB (1)
  • 记录---Vue3对接UE,通过MQTT完成通讯
  • 《Real-Time Rendering》第一章 介绍
  • C语言基础
  • 公益站Agent Router注册送200刀额度竟然是真的
  • 数据集中valid的作用