洛谷4755 Beautiful Pair (分治)
题目描述
小D有个数列 \(a\),当一个数对 \((i,j)(i\le j)\) 满足\(a_i\)和\(a_j\)的积 不大于 \(a_i \cdots a_j\) 中的最大值时,小D认为这个数对是美丽的.请你求出美丽的数对的数量。
输入格式:
第一行输入一个整数 \(n\),表示元素个数。 第二行输入 \(n\) 个整数 \(a_1,a_2,a_3,\cdots,a\),为所给的数列。
输出格式:
输出一个整数,为美丽的数字对数。
其中\(m\le 10^5,a_i \le 10^9\)
这个题,看到题的第一想法就是QwQ,一看就是dp或者一些恶心的数据结构题,但是想了一会dp,发现不太可行。那么我们可以考虑通过**分治** 来解决这个问题
我们考虑,对于一个区间\([l,r]\),我们可以通过这个区间的\(mx\)(表示最大的\(a_i\))来解决,因为要求乘积不能大于区间的最大值,所以我们在 计算区间的时候,\(mx\)一般情况下是不能和别的数进行组合的。所以说,按照mx来分组,那么当前区间的答案就是\([l,mx-1]\)的答案加上\([mx+1,r]\),然后加上两个区间产生的贡献。
QwQ那么怎么求左右区间产生的贡献呢
我们考虑,对于一个固定的左端点\(l\),我们需要在\([mx+1,r]\)区间找出,小于\(\lfloor \frac{mx}{a_l} \rfloor\)的数的个数
然后我们枚举所有的左端点,然后数一数,最后考虑\(mx\)这个端点可以不可以跟左边的这个区间进行组合
那么我们应该怎么求一个区间内小于等于一个数的个数呢
QwQ
呀,BIT!树状数组 嗯!
我们考虑将每个数进行离散化,然后维护一个权值树状数组,然后我们每次只需要\(query(find(\lfloor \frac{mx}{a_l} \rfloor))\)就可以,其中\(find\)的函数的作用是,找到这个值离散化后的小于等于它的最大的值,其实可以直接\(lower_bound\)
记得离散化!
还有一些人会问,这个算法的时间复杂度可以保证吗?QwQ具体的证明我也不是很会呀,不过可以感性l理解
如果我们每次挑选的\(mx\),都选择比较中间的,那么我们可以大致理解是\(O(nlogn)\)的
直接上代码
dfs版的分治
非常理解,个人敢接
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<unordered_map>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 1e5+1e2;
int c[maxn];
int a[maxn];
int b[maxn];
int n,m;
int dp[maxn][23];
int p[maxn];
int tmp = 0;
unordered_map<int,int> mp;
struct Node{
int id,val;
};
Node g[maxn];
int lowbit(int x)
{
return (-x) & x;
}
void update(int x,int p)
{
for (int i=x;i<=n;i+=lowbit(i)) c[i]+=p;
}
long long query(int x)
{
long long ans=0;
for (int i=x;i;i-=lowbit(i)) ans+=(long long)c[i];
return ans;
}
int find(int x)
{
if (x>p[n]) return tmp;
return g[upper_bound(p+1,p+1+n,x)-p-1].id;
}
bool cmp(Node a,Node b)
{
return a.val<b.val;
}
int countmax(int x,int y)
{
int k = log2(y-x+1);
return max(dp[x][k],dp[y-(1 <<k)+1][k]);
}
void init()
{
for (int j=1;j<=21;j++)
for (int i=1;i<=n;i++)
{
if (i+(1 << j)-1<=n)
{
dp[i][j]=max(dp[i][j-1],dp[i+(1 << (j-1))][j-1]);
}
}
}
long long solve(int l,int r)
{
if (l>r) return 0;
if (l==r) {
if (a[l]==1)return 1;
else return 0;
}
long long cnt=0;
int mid = (l+r) >> 1;
int mx = countmax(l,r),pos=0;
for (int i=l;i<=r;i++)
{
if (a[i]==mx) {
if (!pos || abs(mid-i)<abs(mid-pos)) pos=i;
}
}
cnt+=solve(l,pos-1);
cnt+=solve(pos+1,r);
for (int i=pos+1;i<=r;i++) update(b[i],1);
for (int i=l;i<=pos;i++) cnt=cnt+(query(find(mx/a[i])));
for (int i=pos+1;i<=r;i++) update(b[i],-1);
//cout<<l<<" "<<r<<" "<<cnt<<endl;
for (int i=l;i<=pos-1;i++) update(b[i],1);
cnt=cnt+(query(find(mx/a[pos])));
for (int i=l;i<=pos-1;i++) update(b[i],-1);
//out<<l<<" "<<r<<" "<<cnt<<endl;
if (a[pos]==1) cnt++;
return cnt;
}
int main()
{
n=read();
for (int i=1;i<=n;i++) a[i]=read(),g[i].id=i,g[i].val=a[i],dp[i][0]=a[i];
init();
sort(g+1,g+1+n,cmp);
for (int i=1;i<=n;i++)
{
if (g[i].val!=g[i-1].val) tmp++;
b[g[i].id]=tmp;
mp[a[g[i].id]]=tmp;
}
//for (int i=1;i<=n;i++) cout<<b[i]<<" ";
memset(g,0,sizeof(g));
for (int i=1;i<=n;i++) p[i]=a[i],g[i].val=a[i],g[i].id=b[i];
sort(g+1,g+1+n,cmp);
sort(p+1,p+1+n);
//cout<<endl;
long long ans=solve(1,n);
cout<<ans;
return 0;
}
洛谷4755 Beautiful Pair (分治)的更多相关文章
- 洛谷$P4755\ Beautiful\ Pair$ 最大值分治
正解:最大值分治 解题报告: 传送门$QwQ$ 昂考虑如果已经钦定了点$x$是这个$max$了,然后现在要求有多少对$[l,r]$满足$a_x=max\left\{a_i\right\},i\in[l ...
- 洛谷 P4755 - Beautiful Pair(主席树+分治+启发式优化)
题面传送门 wssb,我紫菜 看到这类与最大值统计有关的问题可以很自然地想到分治,考虑对 \([l,r]\) 进行分治,求出对于所有 \(l\le x\le y\le r\) 的点对 \((x,y)\ ...
- 洛谷P3810 陌上花开 CDQ分治(三维偏序)
好,这是一道三维偏序的模板题 当然没那么简单..... 首先谴责洛谷一下:可怜的陌上花开的题面被无情的消灭了: 这么好听的名字#(滑稽) 那么我们看了题面后就发现:这就是一个三维偏序.只不过ans不加 ...
- [洛谷P3806] [模板] 点分治1
洛谷 P3806 传送门 这个点分治都不用减掉子树里的了,直接搞就行了. 注意第63行 if(qu[k]>=buf[j]) 不能不写,也不能写成>. 因为这个WA了半天...... 如果m ...
- 洛谷P4178 Tree (点分治)
题目描述 给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K 输入输出格式 输入格式: N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下 ...
- 洛谷 P3806 (点分治)
题目:https://www.luogu.org/problem/P3806 题意:一棵树,下面有q个询问,问是否有距离为k的点对 思路:牵扯到树上路径的题都是一般都是点分治,我们可以算出所有的路径长 ...
- 洛谷P4390 Mokia CDQ分治
喜闻乐见的CDQ分治被我搞的又WA又T..... 大致思路是这样的:把询问用二维前缀和的思想拆成4个子询问.然后施CDQ大法即可. 我却灵光一闪:树状数组是可以求区间和的,那么我们只拆成两个子询问不就 ...
- 洛谷 CF55D Beautiful numbers 解题报告
CF55D Beautiful numbers 题意 \(t(\le 10)\)次询问区间\([l,r](1\le l\le r\le 9\times 10^{18})\)中能被每一位上数整除的数的个 ...
- Luogu 4755 Beautiful Pair
分治 + 主席树. 设$solve(l, r)$表示当前处理到$[l, r]$区间的情况,我们可以找到$[l, r]$中最大的一个数的位置$mid$,然后扫一半区间计算一下这个区间的答案. 注意,这时 ...
随机推荐
- Spring系列之不同数据库异常如何抽象的?
前言 使用Spring-Jdbc的情况下,在有些场景中,我们需要根据数据库报的异常类型的不同,来编写我们的业务代码.比如说,我们有这样一段逻辑,如果我们新插入的记录,存在唯一约束冲突,就会返回给客户端 ...
- Linux从头学11:理解了这三个概念,才能彻底理解任务管理和任务切换
作 者:道哥,10+年的嵌入式开发老兵. 公众号:[IOT物联网小镇],专注于:C/C++.Linux操作系统.应用程序设计.物联网.单片机和嵌入式开发等领域. 公众号回复[书籍],获取 Linux. ...
- SpringBoot详解(一)——
https://www.cnblogs.com/lifullmoon/p/14957771.html https://www.cnblogs.com/lifullmoon/p/14957751.htm ...
- layui的CRUD案列
用layui来实现一个简单的二级权限和增删改查案列 利用layui提供的组件(table , layer , form,tree)来进行开发 写一个简单的登录界面 根据用户的ID来 获取用户所对应 ...
- window创建l2tp
windows上创建一个L2TP的隧道连接 进入控制面板,打开"网络和共享中心",如下图,之后点击"设置新的连接或网络" 进入到"设置连接或网络&qu ...
- VUE003. 解决data中使用vue-i18n不更新视图问题(computed属性)
案例 在国际化开发中,有一部分需要国际化的文字是由数据驱动的储存在data中,然而VUE的data存在很多无法实时更新视图的问题,比如v-for循环的标签,当数据层次过深,通过源数据数组的索引改变它的 ...
- 清除router路由后面的参数
清除router参数: 1.this.$router.push({ query: {}}) 2.var path = this.$route.path; //获取路由路径 this.$route ...
- 任由文字肆意流淌,更自由的开源 Markdown 编辑器
对于创作平台来说内容编辑器是十分重要的功能,强大的编辑器可以让创作者专注于创作"笔"下生花.而最好取悦程序员创作者的方法之一就是支持 Markdown 写作,因为大多数程序员都是用 ...
- 【优化技术专题】「温故而知新」基于Quartz系列的任务调度框架的动态化任务实现分析
不提XXLJOB或者其他的调度框架,就看我接触的第一个任务调度框架Quartz(温故而知新) Quartz的动态暂停 恢复 修改和删除任务 实现动态添加定时任务,先来看一下我们初步要实现的目标效果图, ...
- vue-自定义指令(directive )的使用方法
前言 在vue项目中我们经常使用到 v-show ,v-if,v-for等内置的指令,除此之外vue还提供了非常方便的自定义指令,供我们对普通的dom元素进行底层的操作.使我们的日常开发变得更加方便快 ...