CF1039E Summer Oenothera Exhibition

LG传送门

根号分治好题。

可以先看我的根号分治总结

题意就是给出长度为\(n\)的区间和\(q\)组询问以及一个\(w\),每次询问一个\(k\),问最少把一段给定区间划分几次可以满足每一段划分出的子区间的极差不超过\(w-k\)(以下默认\(k\)就是\(w-k\))。

这题主要有两种写法,一种是\(O(n \sqrt nlog n)\)的,一种是\(O(n^{ \frac 5 3}+n^{ \frac 4 3} log n)\)的(本文中默认了\(n\)和\(q\)同阶)。

反正先考虑\(n^2\)暴力,预处理一个ST表,对于每次询问\(O(n)\)地贪心扫一遍,能划在一个子区间内就划在一个子区间内,这样贪心一定是正确的。

先讲第一种,比较好想,想到从每个点向从它开始能划到它同一个子区间内的最远的点的右边一个点(定义这个点为\(h[j]\))一条边,这时我们就想起了弹飞绵羊,可以用LCT维护答案,但是由于每次改变\(k\)值会可能更改所有点连边情况,的直接这样做是\(O(n^2 log n)\)的,连暴力都不如。考虑根号分块,先把询问离线,按\(k\)从小到大排个序,这样划分出区间的长度是单调不减的,如果所连的边的长度不超过\(\sqrt n\),就直接用LCT暴力维护,由于边的长度不超过\(\sqrt n\),对于所有点最多删边加边\(\sqrt n\)次,由于每次的复杂度都是\(log n\)的,所以总的复杂度是\(O(n \sqrt n log n)\);如果所要连的边长度超过\(\sqrt n\)了,就不连这条边;这样我们就用LCT维护了一个森林,对于森林里每棵树的贡献都可以\(O(1)\)查询,每次询问时先计算一棵树的贡献,到了树根之后就二分它的\(h[j]\),跳过去把答案++,再计算\(h[j]\)所在树的贡献。

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<vector>
#include<cmath>
#define R register
#define I inline
using namespace std;
const int S=100003,M=17,inf=0x7fffffff;
char buf[1000000],*p1,*p2;
I char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,S,stdin),p1==p2)?EOF:*p1++;}
I int rd(){
R int f=0; R char c=gc();
while(c<48||c>57) c=gc();
while(c>47&&c<58) f=f*10+(c^48),c=gc();
return f;
}
struct query{int k,p,o;}q[S];
struct splay{int p,d[2],s;}e[S];
int a[S],b[S],l[S],f[S][M],g[S][M],h[S],m;
vector<int> v[S];
I int operator<(query x,query y){return x.k<y.k;}
I int min(int x,int y){return x<y?x:y;}
I int max(int x,int y){return x>y?x:y;}
I int qmn(int x,int y){
R int i=l[y-x+1];
return min(f[x][i],f[y-(1<<i)+1][i]);
}
I int qmx(int x,int y){
R int i=l[y-x+1];
return max(g[x][i],g[y-(1<<i)+1][i]);
}
I int nrt(int x){return e[e[x].p].d[0]==x||e[e[x].p].d[1]==x;}
I void psu(int x){e[x].s=e[e[x].d[0]].s+e[e[x].d[1]].s+1;}
I void rtt(int x){
R int f=e[x].p,g=e[f].p,b=e[f].d[1]==x,c=e[x].d[!b];
if(nrt(f)) e[g].d[e[g].d[1]==f]=x;
if(c) e[c].p=f;
e[f].p=x,e[x].p=g,e[x].d[!b]=f,e[f].d[b]=c,psu(f);
}
I void spl(int x){
for(R int f,g;nrt(x);rtt(x)){
f=e[x].p,g=e[f].p;
if(nrt(f))
rtt((e[g].d[1]==f)^(e[f].d[1]==x)?x:f);
}
psu(x);
}
I void acc(int x){
for(R int y=0;x;x=e[y=x].p)
spl(x),e[x].d[1]=y,psu(x);
}
I int frt(int x){
while(e[x].d[0])
x=e[x].d[0];
return x;
}
I void cut(int x){acc(x),spl(x),e[e[x].d[0]].p=0,e[x].d[0]=0,psu(x);}
I int fnd(int x,int k){
R int l=x+1,r=m,m;
for(m=l+r>>1;l<r;m=l+r>>1)
qmx(x,m)-qmn(x,m)>k?r=m:l=m+1;
return m;
}
int main()
R int n=rd(),W=rd(),Q=rd(),p=sqrt(n),i,j,k,t,s,o;
for(m=n+1,i=1;i<=n;++i)
a[i]=f[i][0]=g[i][0]=rd();
a[m]=f[m][0]=g[m][0]=inf,l[1]=0;
for(i=2;i<=m;++i)
l[i]=l[i>>1]+1;
for(i=1,k=2;k<=m;++i,k<<=1)
for(j=1;j+k-1<=m;++j)
f[j][i]=min(f[j][i-1],f[j+(k>>1)][i-1]),g[j][i]=max(g[j][i-1],g[j+(k>>1)][i-1]);
for(i=1;i<=Q;++i)
q[i].p=i,q[i].k=W-rd();
sort(q+1,q+Q+1);
for(i=1;i<=n;++i)
h[i]=i,e[i].p=i+1,v[1].push_back(i);
for(i=1;i<=Q;++i){
for(o=0,t=0,s=v[i].size();t<s;++t){
j=v[i][t],cut(j);
for(k=h[j]+1;qmx(j,k)-qmn(j,k)<=q[i].k&&k-j<=p&&k<=n;++k);
if(k-j>p)
b[j]=1;
else
h[j]=k,e[j].p=h[j],v[lower_bound(q+1,q+1+Q,(query){qmx(j,h[j])-qmn(j,h[j]),0})-q].push_back(j);
}
for(j=1;;j=fnd(j,q[i].k),++o){
if(!b[j])
acc(j),spl(j),o+=e[j].s-1,j=frt(j);
if(j>n)
break;
}
q[q[i].p].o=o-1;
}
for(i=1;i<=Q;++i)
printf("%d\n",q[i].o);
return 0;
}

注意在实现时有一个重要的细节,删边加边的时候必须二分找它下一个可能改变它\(h[j]\)值的询问,而不能对于每次询问都把序列扫一遍,这样复杂度会被卡成\(n^2\)。例如下面的操作就是错误的。

    for(i=1;i<=Q;++i){
o=0;
for(j=1;j<=n;++j)
if(!b[j]){
for(k=h[j];qmx(j,k)-qmn(j,k)<=q[i].k&&k-j<=p&&k<=n;++k);
if(k-j>p)
b[j]=1,cut(j);
else
if(k^h[j])
cut(j),h[j]=k,e[j].p=h[j];
}
for(j=1;;j=fnd(j,q[i].k),++o){
if(!b[j])
acc(j),spl(j),o+=e[j].s-1,j=frt(j);
if(j>n) break;
}
q[q[i].p].o=o-1;
}

下面介绍第二种做法。

wxh太神了我看不懂。

然后Itst把wxh的做法写出来了,我就直接丢链接了。

CF1039E Summer Oenothera Exhibition 根号分治,LCT,ST表的更多相关文章

  1. [CF1039E]Summer Oenothera Exhibition[根号分治+lct]

    题意 给一个长度为 \(n\) 的序列, \(q\) 次询问,次给一个 \(k_i\) ,问最少将序列划分成多少次,满足每一段的极差不超过\(w−k_i\). \(1 \leq n, q \leq 1 ...

  2. CF1039E Summer Oenothera Exhibition 贪心、根号分治、倍增、ST表

    传送门 感谢这一篇博客的指导(Orzwxh) $PS$:默认数组下标为$1$到$N$ 首先很明显的贪心:每一次都选择尽可能长的区间 不妨设$d_i$表示在取当前$K$的情况下,左端点为$i$的所有满足 ...

  3. 【BZOJ3784】树上的路径 点分治序+ST表

    [BZOJ3784]树上的路径 Description 给定一个N个结点的树,结点用正整数1..N编号.每条边有一个正整数权值.用d(a,b)表示从结点a到结点b路边上经过边的权值.其中要求a< ...

  4. CF587F-Duff is Mad【AC自动机,根号分治】

    正题 题目链接:https://www.luogu.com.cn/problem/CF587F 题目大意 给出\(n\)个字符串\(s\).\(q\)次询问给出\(l,r,k\)要求输出\(s_{l. ...

  5. Codeforces 1039D You Are Given a Tree [根号分治,整体二分,贪心]

    洛谷 Codeforces 根号分治真是妙啊. 思路 考虑对于单独的一个\(k\)如何计算答案. 与"赛道修建"非常相似,但那题要求边,这题要求点,所以更加简单. 在每一个点贪心地 ...

  6. BZOJ.4320.[ShangHai2006]Homework(根号分治 分块)

    BZOJ \(\mathbb{mod}\)一个数\(y\)的最小值,可以考虑枚举剩余系,也就是枚举区间\([0,y),[y,2y),[2y,3y)...\)中的最小值(求后缀最小值也一样)更新答案,复 ...

  7. BZOJ3351: [ioi2009]Regions(根号分治)

    题意 题目链接 Sol 很神仙的题 我们考虑询问(a, b)(a是b的祖先),直接对b根号分治 如果b的出现次数\(< \sqrt{n}\),我们可以直接对每个b记录下与它有关的询问,这样每个询 ...

  8. [CF587F]Duff is Mad[AC自动机+根号分治+分块]

    题意 给你 \(n\) 个串 \(s_{1\cdots n}\) ,每次询问给出 \(l,r,k\) ,问在 \(s_{l\cdots r}\) 中出现了多少次 \(s_k\) . \(n,q,\su ...

  9. [CF1039D]You Are Given a Tree[贪心+根号分治]

    题意 给你\(n\)个点的树,其中一个简单路径的集合被称为\(k\)合法当且仅当树的每个节点最多属于一条路径,且每条路径包含\(k\)个节点.对于每个\(k(k \in [1,n])\),输出最多的\ ...

随机推荐

  1. win7下解决vs2015新建项目,提示“未将对象引用设置到引用实例“的问题

    问题描述: 打开vs2015新建c++项目时,出现有如下内容的对话框“未将对象引用设置到引用实例”的提示 解决方法: 1.  温馨提示:千万不要一冲动,就去卸载vs2015!! win7下安装vs20 ...

  2. 用VisualAssist在Visual Studio上设置快捷键快速编程

    一  在Visual Studio上安装VisualAssist工具: 链接:https://pan.baidu.com/s/1uaeRFTvY4p7LNoDvQFEU5A 提取码:j1ws 安装后, ...

  3. Hive分组取Top N

    Hive在0.11.0版本开始加入了row_number.rank.dense_rank分析函数,可以查询分组排序后的top值   说明: row_number() over ([partition ...

  4. Sql server 索引详解

    参考资料:老K写的,http://www.cnblogs.com/AK2012/archive/2013/01/04/2844283.html SQL索引在数据库优化中占有一个非常大的比例, 一个好的 ...

  5. js实现浏览器用户信息收集

    前言 这是一个通过html5,javascript用于收集用户通过上网泄漏的各种信息,包括地理位置,IP地址,照片,语音,浏览器版本等信息.结合大数据,可实现广告定向投放,用户追踪,用户行为分析,用户 ...

  6. Centos7 用yum命令安装LAMP环境(php+Apache+Mysql)以及php扩展

    1.yum -y update    // 更新系统 1.1)yum -y install gcc g++ gcc-c++ make kernel-devel kernel-headers 1.2)v ...

  7. C#多线程的用法9-Semaphore

    Semaphore:可理解为允许线程执行信号的池子,池子中放入多少个信号就允许多少线程同时执行. private static void MultiThreadSynergicWithSemaphor ...

  8. C#中virtual(虚方法)的理解以及和abstract(抽象方法)的区别

    Virtual方法(虚方法) virtual 关键字用于在基类中修饰方法.virtual的使用会有两种情况: 情况1:在基类中定义了virtual方法,但在派生类中没有重写该虚方法.那么在对派生类实例 ...

  9. 《SQL Server 2008从入门到精通》--20180717

    目录 1.触发器 1.1.DDL触发器 1.2.DML触发器 1.3.创建触发器 1.3.1.创建DML触发器 1.3.2.创建DDL触发器 1.3.3.嵌套触发器 1.3.4.递归触发器 1.4.管 ...

  10. [Python_2] Python 基础

    0. 说明 Python 基础笔记,使用的版本为 Python 3.6.2 Python 的变量.字符串操作.list.元组.字典.循环.range.类型转换.运算等操作. 1. 引号的使用 字符串使 ...