点分治

点分治讲解

解决树上路径问题

经典例题:点分治(长度小于m的路径计数)
经典例题:点分治(聪聪可可)
经典例题:点分治(多个定值路径计数)
经典例题:点分治(采药)
经典例题:点分治+ST表+优先队列
经典例题:点分治+FFT+概率期望
经典例题:点分治+01分数规划
舒老师推荐点分治(难)

注意:点分治的常数比较大
每个点都会作为重心,然而每个点的遍历次数不止一次

点分治有两种写法:

  • 无脑计算整棵子树中的路径,减去路径端点在同一棵子树中的情况
    (主要用于路径计数)

  • 依次遍历子树,记录当前子树和之前子树之间的路径
    (主要用于复杂的情况,或者说是不容易去重的情况:dp,采药人,点分治+ST+优先队列)

点分治中比较重要的就是 findroot f i n d r o o t findroot
附赠长度 <=m <= m <script type="math/tex" id="MathJax-Element-34"><=m</script>的路径计数,长度 =m = m =m的路径计数

int size[N],f[N],root,sz;
bool vis[N];void findroot(int now,int fa) {f[now]=0;size[now]=1;for (int i=st[now];i;i=way[i].nxt)if (way[i].y!=fa&&!vis[way[i].y]) {findroot(way[i].y,now);size[now]+=size[way[i].y];f[now]=max(f[now],size[way[i].y]);}f[now]=max(f[now],sz-size[now]);if (f[now]<f[root]) root=now;
}void init(int x) {root=0; f[0]=N;sz=size[x];            //当前子树的大小
}

长度 <=m <= m <script type="math/tex" id="MathJax-Element-36"><=m</script>的路径计数
注意 i++,j−− i + + , j − − i++,j--

int d[N],cnt,deep[N];void dfs(int now,int fa) {d[++cnt]=deep[now];for (int i=st[now];i;i=way[i].nxt)if (way[i].y!=fa&&!vis[way[i].y]) {deep[way[i].y]=deep[now]+way[i].v;dfs(way[i].y,now);}
} int cal(int now,int dep,int opt) {cnt=0;deep[now]=dep;dfs(now,0);sort(d+1,d+1+cnt); int i=1,j=cnt,ans=0;while (i<j) {if (d[i]+d[j]<=m) ans+=(j-i),i++;else j--;}return ans*opt;
}

长度 =m = m =m的路径计数
注意 l++,r−− l + + , r − − l++,r--,if (l>=r) break;

int cal(int now,int dep,int opt) {cnt=0;deep[now]=dep;dfs(now,0);sort(d+1,d+1+cnt);int l=1,r=cnt,ans=0;while (l<r) {while (d[l]+d[r]>m&&l<r) r--;if (l>=r) break;int rr=r;while (d[l]+d[r]==m) {ans++; r--;if (l>=r) break; }r=rr;l++;if (l>=r) break; }return ans*opt;
}

CDQ分治

CDQ分治简单讲解

CDQ分治是我们处理各类问题的重要武器
优势在于可以顶替复杂的高级数据结构,而且常数比较小
缺点在于必须离线操作

经典例题:CDQ(三位偏序入门)
经典例题:CDQ(矩阵和)
经典例题:CDQ(最近点对||KDtree)
经典例题:CDQ+dp(纸箱)
经典例题:CDQ+dp
经典例题:CDQ(动态逆序对)
经典例题:CDQ+dp+FFT(一)
经典例题:CDQ+dp+FFT(二)
经典例题:CDQ+dp(维护凸壳【难)

细节一

CDQ一句话总结:左区间影响右区间
CDQ分治有两种写法:

//----法一-----
CDQ(L,M)
CDQ(M+1,R)solve//----法二----
CDQ(L,M)solveCDQ(M+1,R)

感谢舒老师为我解答了困惑

仔细想了一下
例如陌上花开这种题,只需要统计,我们就可以归并
然而如果是dp需要CDQ分治辅助,我们在处理 [mid+1,r] [ m i d + 1 , r ] [mid+1,r]的时候需要 [l,mid] [ l , m i d ] [l,mid]的 f f f值已经维护好了
所以我们先要CDQ(l,mid)" role="presentation" style="position: relative;">CDQ(l,mid)CDQ(l,mid)CDQ(l,mid),处理好这一部分之后
把相应的贡献转移到 f[mid+1] f [ m i d + 1 ] f[mid+1]上,再 CDQ(mid+1,r) C D Q ( m i d + 1 , r ) CDQ(mid+1,r)

CDQ分治的变化还是很多的,只能给出一部分情况的总结

//先归并后处理 struct node{a,b,c,num;
}p[N],q[N];
//a 第一维
//b 需要归并的第二维
//c 树状数组
//num 按a排序时的位置 void CDQ(int l,int r) {if (l==r) return;int mid=(l+r)>>1;CDQ(l,mid);CDQ(mid+1,r);int t1=l,t2=mid+1;for (int i=l;i<=r;i++)if ((t1<=mid&&p[t1].b<=p[t2].b)||t2>r)q[i]=p[t1++];else q[i]=p[t2++];for (int i=l;i<=r;i++) {p[i]=q[i];if (p[i].num<=mid) ...   //加值 else ...                 //维护ans } for (int i=l;i<=r;i++)if (p[i].num<=mid) ...   //清空
}//边归并边处理struct node{a,b,c,type;
}p[N],q[N];void CDQ(int l,int r) {if (l==r) return;int mid=(l+r)>>1;CDQ(l,mid);CDQ(mid+1,r);sort(p+l,p+mid+1,cmp);      //sort会比归并慢  按照第二维排序 sort(p+mid+1,p+r+1,cmp);//我之所以这么做是因为我们没有定义num,不能把两部分打乱 int t1=l,t2=mid+1,last=0;while (t2<=r) {while (t1<=mid&&p[t1].type!=1) t1++;    //左区间找修改while (t2<=r&&p[t2].type!=2) t2++;      //右区间找询问if (t1<=mid&&p[t1].b<=p[t2].b) {...last=t1;                             //记录处理到的修改 } else if (t2<=r) {...}} for (int i=l;i<=last;i++)if (p[i].type==1) ...                     //清除影响
}

整体二分

感觉整体二分和CDQ分治比较像,所以就放到一起啦

整体二分简单讲解

所谓整体二分,需要数据结构题满足以下性质:
1. 询问的答案具有可二分性
2. 修改对判定答案的贡献相对独立,修改之间互不影响效果
3. 修改如果对判定答案有贡献,则贡献为一确定的与判定标准无关的值
4. 贡献满足交换律,结合律,具有可加性
5. 题目允许离线操作

询问的答案有可二分性质显然是前提,我们发现,因为修改对判定标准的贡献相对独立,且贡献的值(如果有的话)与判定标准无关,所以如果我们已经计算过某一些修改对询问的贡献,那么这个贡献永远不会改变,我们没有必要当判定标准改变时再次计算这部分修改的贡献,只要记录下当前的总贡献,再进一步二分时,直接加上新的贡献即可
这样的话,我们发现,处理的复杂度可以不再与序列总长度直接相关了,而可能只与当前待处理序列的长度相关

经典例题:整体二分(区间第k大)
经典例题:整体二分(区间第k大+单点修改)
经典例题:整体二分+线段树(区间第k大+区间添加)
经典例题:整体二分(二维矩阵第k大+奇技淫巧)

细节一

我们需要把题目中的信息都转化为修改信息或者询问信息
(初始信息转化为添加操作,修改信息转化为一个删除一个添加)

细节二

有的时候我们需要把修改和查询一起二分
例题

举个简单的例子:求区间第 k k k大
二分答案M" role="presentation" style="position: relative;">MMM
我们一般习惯 O(n) O ( n ) O(n)扫一遍所有的操作,遇到修改就修改,遇到询问就询问

不过这样做有一个前提:对于每个询问,能够影响到ta的修改一定在ta之前就处理过了

显然值 <=M <= M <script type="math/tex" id="MathJax-Element-49"><=M</script>的修改需要处理,并且分到左区间, k<= k <= k “ 当前区间内有的元素个数 ” 的询问分到左区间
其余的操作分到右区间

这里又要注意啦,分到右区间的询问一定会受左区间操作的影响
但是已经分到左区间的修改操作在继续二分的过程中是不会再在右区间出现了

所以这种情况下,对于分到右区间的询问我们需要记录下左区间操作对ta的影响
(在询问区间第 k k k大的题目中,就是:k−=值小于M的元素个数" role="presentation" style="position: relative;">k−=值小于M的元素个数k−=值小于M的元素个数k-=值小于M的元素个数)

有的时候我们不需要把修改二分,只需要一个变量记录处理到了哪个操作即可
例题

这种方法用于修改繁琐的情况
二分答案 M M M
移动修改指针,使得只有小于M的操作处理了
按照k" role="presentation" style="position: relative;">kkk和元素个数的关系,划分区间

不过这里要注意
因为我们不二分操作,所以对于右区间的询问我们保持原样

细节三

整体二分多和树状数组结合(例如记录区间内有多少小于M的元素)
但是不要忘了树状数组只支持两套操作(不能混用)

  • 单点修改+区间查询

  • 区间修改+单点查询

如果我们遇到了需要区间加并且区间查询的操作,可以考虑用线段树维护

细节四

流程:

if (l>r) return;
if (L==R) 记录答案处理操作最后不要忘了依次消除影响(不能无脑清零,复杂度会降低)solve(L,M)
solve(M+1,R)

(同时二分修改和询问)

const int INF=1e9+7;
const int N=200010;
struct node{int x,y,k,id,type;
};
node q[N],q1[N],q2[N];
int n,m,tot,ans[N],mn,mx,c[N];//在位置上changevoid add(int x,int z) {for (int i=x;i<=n;i+=(i&(-i))) c[i]+=z;}
int ask(int x) {int ans=0;for (int i=x;i>0;i-=(i&(-i))) ans+=c[i];return ans;
}//对于每个询问,能够影响到ta的修改一定在ta之前就处理过了 void solve(int l,int r,int L,int R) {if (l>r) return;if (L==R) {for (int i=l;i<=r;i++)if (q[i].type==2)ans[q[i].id]=L;return;}int M=(L+R)>>1;int t1=0,t2=0;for (int i=l;i<=r;i++) if (q[i].type==1) {if (q[i].y<=M) {add(q[i].x,1);q1[++t1]=q[i];}else q2[++t2]=q[i];}else {int sum=ask(q[i].y)-ask(q[i].x-1);if (sum>=q[i].k)q1[++t1]=q[i];else {q[i].k-=sum;            //记录影响 q2[++t2]=q[i];}}for (int i=1;i<=t1;i++) if (q1[i].type==1) add(q1[i].x,-1);for (int i=1;i<=t1;i++) q[l+i-1]=q1[i];for (int i=1;i<=t2;i++) q[l+t1+i-1]=q2[i];solve(l,l+t1-1,L,M);solve(l+t1,r,M+1,R);
}int main()
{tot=0; mn=INF,mx=-INF;scanf("%d%d",&n,&m);for (int i=1;i<=n;i++) {        //修改 tot++;scanf("%d",&q[tot].y);q[tot].x=i; q[tot].type=1;mn=min(mn,q[tot].y);mx=max(mx,q[tot].y);}for (int i=1;i<=m;i++) {tot++;scanf("%d%d%d",&q[tot].x,&q[tot].y,&q[tot].k);q[tot].id=i; q[tot].type=2;}solve(1,tot,mn,mx);for (int i=1;i<=m;i++) printf("%d\n",ans[i]);return 0;
}

(二分修改)

const int INF=1e9+7;
const int N=200010;
struct node{int x,y,k,id;
}q[N],q1[N],q2[N],a[N];
int n,m,T,mn,mx;
int cnt_a,cnt_q,ans[N],c[N];int cmp(const node &a,const node &b) {return a.y<b.y;
}void add(int x,int z) {for (int i=x;i<=n;i+=(i&(-i))) c[i]+=z;}
int ask(int x) {int ans=0;for (int i=x;i>0;i-=(i&(-i))) ans+=c[i];return ans;
}void solve(int l,int r,int L,int R) {if (l>r) return;if (L==R) {for (int i=l;i<=r;i++)ans[q[i].id]=L;return;}int M=(L+R)>>1;while (a[T+1].y<=M&&T<n) T++,add(a[T].x,1);while (a[T].y>M&&T>=1) add(a[T].x,-1),T--;int t1=0,t2=0;for (int i=l;i<=r;i++) {int sum=ask(q[i].y)-ask(q[i].x-1);if (q[i].k<=sum) q1[++t1]=q[i];else q2[++t2]=q[i];}for (int i=1;i<=t1;i++) q[l+i-1]=q1[i];for (int i=1;i<=t2;i++) q[l+t1+i-1]=q2[i];solve(l,l+t1-1,L,M);solve(l+t1,r,M+1,R);
}int main()
{cnt_a=cnt_q=0; mn=INF,mx=-INF;scanf("%d%d",&n,&m);for (int i=1;i<=n;i++) {        //修改 scanf("%d",&a[i].y);a[i].x=i; mn=min(mn,a[i].y);mx=max(mx,a[i].y);}for (int i=1;i<=m;i++) {scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].k);q[i].id=i;}sort(a+1,a+1+n,cmp);T=0;solve(1,m,mn,mx);for (int i=1;i<=m;i++) printf("%d\n",ans[i]);return 0;
} 

点分治+CDQ分治+整体二分全纪录相关推荐

  1. [总结]CDQ分治整体二分

    从昨天到现在除了90%的颓废时间一直在研究一些分治的姿势,主要就是CDQ分治和整体二分. 首先推荐一些学习资料: 陈丹琦 <从 < Cash > 谈一类分治算法的应用> 许昊然 ...

  2. 【CF603E】Pastoral Oddities cdq分治+并查集

    [CF603E]Pastoral Oddities 题意:有n个点,依次加入m条边权为$l_i$的无向边,每次加入后询问:当前图是否存在一个生成子图,满足所有点的度数都是奇数.如果有,输出这个生成子图 ...

  3. CDQ分治【分治(真得头疼)

    前言: 说实话我对于这种没有固定板子,变化多端的算法实在是非常头疼的 但是不学不行,这也是一种很重要的思伟方式 所以趁着这几天的心情比较好(快要放大周),赶紧学一波~ 鸣谢:tham,stdcall ...

  4. 浅谈CDQ分治与偏序问题

    初识CDQ分治 CDQ分治是一个好东西,一直听着dalao们说所以就去学了下. CDQ分治是我们处理各类问题的重要武器.它的优势在于可以顶替复杂的高级数据结构,而且常数比较小:缺点在于必须离线操作. ...

  5. CDQ分治——学习笔记

    前言 因为中国集训队的大佬们总会发明一些新的东西,所以中国的计算机竞赛水平才会一直处于国际前沿,而cdq分治也是某一年的集训队成员发明的算法. 什么是CDQ分治 cdq分治是一种分治(这不是废话嘛), ...

  6. 三维偏序/cdq分治/

    三维偏序---cdq分治 cdq分治概述 二维偏序概述 二维偏序例题分析 三维偏序概述 例题分析 cdq分治概述 前置知识:(如果不懂要先去了解分治) > 分治: > 分而治之,将原问题不 ...

  7. CDQ分治(二维CDQ 、三维CDQ+树状数组、四维CDQ+CDQ+树状数组)

    CDQ分治 CDQ分治相较于普通分治,多了左区间处理后对于右区间的影响. 利用这一点,CDQ分治可以用来做很多数据结构的题目(树状数组.线段树),加一个log的时间复杂度来优化一维. 操作: 假设有两 ...

  8. CDQ 分治与整体二分

    CDQ 分治与整体二分 CDQ 分治 主要是一种分治思想,常用于解决偏序问题. 例如三维偏序问题,我们采用的方法是先处理以第一关键字为区分的左区间.右区间内的答案,再处理左右区间互不干涉的答案. 四维 ...

  9. [CDQ分治与整体二分]个人对CDQ分治与整体二分的理解

    在线/离线:首要考虑 在线算法: 可以以序列化的方式一个一个的处理输入,不必事先知道所有输入数据 离线算法: 必须事先知道所有的输入数据 (例如选择排序就是一个离线算法,而插入排序则不是) 众所周知, ...

最新文章

  1. MATLAB与图像处理(四):将图片序列转化为视频文件,将视频文件转化为图片
  2. Ubuntu Server 16.04 LTS上安装Docker(使用脚本的方式)
  3. 2017.4.20实验三:4、定义一个带有默认值参数的函数,通过传递不同个数的参数值,调用该函数。...
  4. IBM 收购 RedHat(红帽)!340 亿美元
  5. xos详解5:PendSV_Handler
  6. Luogu4921/4931 情侣?给我烧了! 组合、递推
  7. python散点图拟合曲线如何求拟合_python 拟合曲线并求参
  8. 翻译:控制容器的反转IoC和依赖注入模式DIP 概念发源地 Martin Fowler
  9. 小学计算机课知识点内容,小学信息技术教案九篇
  10. VS中读取NMEA数据进行定位精度分析
  11. 人工智能名片小程序如何开启连锁实体店全新移动电商新时代?
  12. 全球首个CTLA-4抑制剂逸沃在中国上市;全球首个原发性轻链型淀粉样变治疗药物兆珂速在华获批 | 医药健闻...
  13. 建筑行业现行相关税收政策及优惠措施汇编
  14. ROS1云课- 1 0 2 4
  15. SU插件情报局 | Laubwerk智能代理植物插件(附插件获取)
  16. 环境工程原理知识重点归纳
  17. Jsp的四大作用域与九大对象
  18. Windows之内存映射文件
  19. 计算机科学与技术学习路线编程基础四大件应用实践编程(含C++学习路线)
  20. 图片路径不存在,替换问题图片

热门文章

  1. python pdf分割_Python分割PDF
  2. html四舍五入函数,Javascript四舍五入(Math.round()与Math.pow())
  3. 小程序博客(云开发)
  4. 财务需求分析师需要了解的财务知识
  5. (转载)酷炫桌面背景图片,实用命令图片
  6. 基于51单片机智能温度控制器温控系统(毕设课设)
  7. Typora快捷键大全(含Windows和mac)!提升你的写作效率
  8. Jetson TX2 arm板子刷机,安装Caffe
  9. bitset简单用法
  10. 2.2.1 Nginx高性能负载均衡器