https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1810

题目给出一个1~n的排列,问有多少连续区间。连续区间的定义为区间内元素排序后之间间隔为1。

对于一个区间[l,r],令mid=(l+r)/2,我们如果能在O(n)内求解出左端点在[l,mid],右端点在[mid+1,r]的连续区间数量,就可以将问题一分为二,递归求解[l,mid] [mid+1,r]。

现在来求解上面所说的这个子问题,首先默认i<j,有一个结论max[i~j]-min[i~j]==j-i时[i,j]是一个连续区间。所以我们维护两组从mid(mid+1)出发,向左(右)延伸的后(前)缀max和min数组。max[i~j]=max(max[i],max[j]),min同理。我们只要找到所有i,j组合使得结论式成立即可。

但是很明显朴素枚举是n^2的,我们能不能优化到n呢?

观察max[i~j],min[i~j],他们的来源有4种组合,因为是两两对应的,我们其中两个组合来讨论,另外两个可以同理推导。

第一种是max和min都来自mid左边。有max[i]-min[i]=j-i,推导得 j=max[i]-min[i]+i,只要枚举左半边的i,并判断j是否合法即可。

第二种是max来自左边min来自右边。有max[i]-min[j]=j-i,推导得max[i]+i=min[j]+j,枚举每个max[i]+i,计算有多少合法的j符合即可。说得轻松,这个j的数量怎么计算呢?

我们发现max想要来自左边,需要满足max[i]>max[j],同理min[i]>min[j]。同时我们发现从中心向外发散,max递增,min递减,也就是从中心向左枚举i的过程中max限制越来越宽松,min越来越严格。我们lp,rp来维护右半区间中满足当前i的[mid+1,r]的j的子窗口,可以预见的是随着i--,这个窗口会向右尺取。我们每加入一个j,就让一个计数数组中的cnt[min[j]+j]++,每剔除一个j则相应减减。对于每个i,完成尺取后,ans+=cnt[max[i]+i]。

对应搞定剩下两个情况后这题就理论AC了,剩下还有一些细节,比如l==r的处理,cnt数组的复原处理,输入挂优化什么的,搞搞就AC了。

#include <iostream>
#include <cmath>
#include <algorithm>
#include <map>
#include <cstring>
#define LL long long
using namespace std;
const LL N = ;
int num[N],n;
LL ans;
int mx[N], mi[N];
int read() {
char ch;
for (ch = getchar(); ch<'' || ch>''; ch = getchar());
int x = ch - '';
for (ch = getchar(); ch >= ''&&ch <= ''; ch = getchar()) x = x * + ch - '';
return x;
}
void make_pre(LL l, LL r, LL mid)
{
mi[mid] = mx[mid] = num[mid];
mi[mid + ] = mx[mid + ] = num[mid + ];
for (int i = mid - ; i >= l; i--)
{
mx[i] = max(num[i], mx[i + ]);
mi[i] = min(num[i], mi[i + ]);
}
for (int i = mid + ; i <= r; i++)
{
mx[i] = max(num[i], mx[i - ]);
mi[i] = min(num[i], mi[i - ]);
}
}
struct tong
{
LL cnt[N * ];
void clear()
{
memset(cnt, , sizeof(cnt));
}
void setZero(LL num)
{
cnt[num + N] = ;
}
void add(LL num,int v)
{
cnt[num + N]+=v;
}
LL query(LL num)
{
return cnt[num+N];
}
}cnt; void solve(LL l, LL r)
{ int temp = ans;
LL mid = (l + r) /;
make_pre(l, r, mid);
//same i
for (int i = l; i <= mid; i++)
{
int nj = mx[i] - mi[i]+i;
if (nj>mid&&nj<=r&&mx[i]>mx[nj] && mi[i]<mi[nj]) ans++;
}
//same j
for (int i = mid+; i <= r; i++)
{
int nj = mx[i] - mi[i]-i;
nj = -nj;
if (nj<=mid&&nj>=l&&mx[i]>mx[nj] && mi[i]<mi[nj]) ans++;
}
//dif mx[i],mi[j]
LL pl = mid+,pr=mid+;
for (int i = mid; i>=l; i--)
{
while (pr <= r&&mx[pr] < mx[i])cnt.add(mi[pr] + pr,),pr++;
while (pl < pr&&mi[pl] > mi[i])cnt.add(mi[pl] + pl, -), pl++;
//if (cnt.query(mx[i] + i) < 0) cout << l << ' ' << r << ' ' << pl << endl;
ans += cnt.query(mx[i] + i);
}
while (pl < pr)cnt.setZero(mi[pl]+pl),pl++;
//dif mi[i],mx[j]
pl = mid , pr = mid;
for (int i=mid+; i<=r; i++)
{
while (pr >=l&&mx[pr] < mx[i])cnt.add(mi[pr] - pr, ), pr--;
while (pl > pr&&mi[pl] > mi[i])cnt.add(mi[pl] - pl, -), pl--;
//if(cnt.query(mx[i] - i)<0)cout << l << ' ' << r << ' ' << pl << endl;
ans += cnt.query(mx[i] - i);
}
while (pl > pr)cnt.setZero(mi[pl] - pl), pl--;
//cout << ans - temp << ' ' << l << ' ' << r << endl;
if (l == r)return;
solve(l, mid);
solve(mid + , r);
}
int main()
{
//cin.sync_with_stdio(false);
n = read();
for (int i = ; i < n; i++)num[i]=read();
ans = ;
cnt.clear();
solve(, n - );
printf("%lld\n", ans+n); return ;
}

51Nod 1810 连续区间的更多相关文章

  1. 51NOD 1810 连续区间 分治 区间计数

    1810 连续区间 基准时间限制:1.5 秒 空间限制:131072 KB 分值: 80     区间内所有元素排序后,任意相邻两个元素值差为1的区间称为“连续区间” 如:3,1,2是连续区间,但3, ...

  2. 51nod 1094 和为k的连续区间【前缀和/区间差/map】

    1094 和为k的连续区间 基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题  收藏  关注 一整数数列a1, a2, ... , an(有正有负),以及另一个整数k ...

  3. 51Nod 1094 和为k的连续区间 | 水

    Input示例 6 10 1 2 3 4 5 6 Output示例 1 4 #include "cstdio" #include "algorithm" #in ...

  4. 51Nod 1094 和为k的连续区间

    #include <iostream> #include <algorithm> #include <cstring> using namespace std; t ...

  5. 51Nod 和为k的连续区间

    一整数数列a1, a2, ... , an(有正有负),以及另一个整数k,求一个区间[i, j],(1 <= i <= j <= n),使得a[i] + ... + a[j] = k ...

  6. 【51Nod 1244】莫比乌斯函数之和

    http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1244 模板题... 杜教筛和基于质因子分解的筛法都写了一下模板. 杜教筛 ...

  7. 51Nod 1268 和为K的组合

    51Nod  1268  和为K的组合 1268 和为K的组合 基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题 给出N个正整数组成的数组A,求能否从中选出若干个,使 ...

  8. 51Nod 1428 活动安排问题

    51Nod   1428  活动安排问题 Link: http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1428 1428 活 ...

  9. 51Nod 1278 相离的圆

    51Nod 1278 相离的圆 Link: http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1278 1278 相离的圆 基 ...

随机推荐

  1. Nodejs学习笔记2

    在linux中, 个人用户的文件, 通常是放在 自己的 家目录中的, root用户放在 /root中. root用户根其他普通用户不同, root用户是专门放在 /root目录中的, 而普通用户的文件 ...

  2. Elasticsearch 异常处理

    cluster_block_exception https://stackoverflow.com/questions/50609417/elasticsearch-error-cluster-blo ...

  3. How to resize slide dimensions without resizing any objects on the slide?

    IF you are competent to unzip the pptx file and modify the XML it can be done, the slide size will c ...

  4. LOJ#2452. 「POI2010」反对称 Antisymmetry

    题目描述 对于一个 \(0/1\) 字符串,如果将这个字符串 \(0\) 和 \(1\) 取反后,再将整个串反过来和原串一样,就称作「反对称」字符串.比如 \(00001111\) 和 \(01010 ...

  5. C#Winform工具箱简介

    BindingSource:指定支持事务处理初始化Button:[按钮]用户单击它时引发事件 CheckBox:[复选框]允许用户选择或清除关联选项 CheckedListBox:[复选列表框]显示一 ...

  6. 分布式爬虫scrapy-redis中settings.py中的配置信息

    SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 使用scrapy-redis的调度器 ITEM_PIPELINES = { 'sc ...

  7. npm介绍和使用

    # npm 介绍 > 概念 : node 包管理工具 > 作用 : 通过 npm 来快速下载/安装项目中依赖的包 > 查看 版本号 : npm -v     # npm 基本使用演示 ...

  8. java笔试总结

    1. Java的IO操作中有面向字节(Byte)和面向字符(Character)两种方式.面向字节的操作为以8位为单位对二进制的数据进行操作,对数据不进行转换,这些类都是InputStream和Out ...

  9. Graphics for R

    https://cran.r-project.org/web/views/Graphics.html CRAN Task View: Graphic Displays & Dynamic Gr ...

  10. React创建组件的三种方式及其区别

    内容转载于http://www.cnblogs.com/wonyun/p/5930333.html React推出后,出于不同的原因先后出现三种定义react组件的方式,殊途同归; 具体的三种方式: ...