Codeforces 193D Two Segments 解题报告

先是在蓝桥杯的网站上看到一道题:

  给出1~n的一个排列,求出区间内所有数是连续自然数的区间的个数。n<=50000。

 

由于数据较弱,即使用O(N^2)的算法也能拿到满分。

 

于是在CF上发现了这一题:

  给一个1~n的排列,在这个排列中选出两段区间,求使选出的元素排序后构成公差为1的等差数列的方案数。选出的两段区间中元素构成的集合相同时视为同一种方案。N<=300000。

 

可以发现CF的这题是上一题的强化版。

 

虽然蓝桥的题目是简化版,但正确的做法似乎没有CF这道题容易想到。

蓝桥的题首先想到的应该是枚举区间,判断区间最大值与最小值的差是否等于区间长度,这样时间复杂度是O(N^2)。

似乎不知道该如何优化。

因此先从CF这题考虑一个比较容易想到的做法:

  考虑自然序列[l,r],假设已经知道[l,r]在读入序列中被分成了几段(假设f[l,r]=k),那么在加入数l-1时,只需判断l-1这个数是单独构成一段(f[l-1,r]=k+1),还是并入了其中一段(f[l-1,r]=k),或者连接了两段(f[l,r]=k-1).当k等于1或2时就可以累加答案.使用这样的方法同样只需要枚举区间左右端点就可以得到一个O(n^2)的算法.再进一步考虑自然序列[l,i],(l<=i<=n).假设t=l-1,在读入序列的左右分别是数x和y(假设x<y).那么对应的f[l-1,m] 中m对应的区间要进行相应的更新(+1,-1),这里可以用线段树来维护.和查询.如此只需要n到1循环l,可以在O(nlogn)的时间复杂度实现.

  对于蓝桥杯的题,假设当前枚举的数是i令[l,r],为f[i,l~r]数值区间.线段树需要维护的东西有区间内最小值,区间内等于最小值的数的个数.CF题需要额外维护,等于区间最小值+1的数的个数.

 

蓝桥杯的代码:

#include <iostream>
#include <cstdio>
using namespace std;
#define lson x<<1
#define rson x<< 1 | 1
const int MAXN = 50009;
struct Segtree {
    int l, r, val, add, tol;
} St[MAXN << 2];
int p[MAXN], a[MAXN];
int n, ans;
void Build (int x, int l, int r) {
    St[x].l = l, St[x].r = r;
    St[x].tol = r - l + 1;
    if (l == r) return;
    int mid = (l + r) >> 1;
    Build (lson, l, mid), Build (rson, mid + 1, r);
}
void push (int x) {
    if (St[x].add == 0) return;
    St[lson].add +=St[x].add, St[rson].add += St[x].add;
    St[lson].val += St[x].add, St[rson].val += St[x].add;
    St[x].add = 0;
}
void update (int x) {
    St[x].val = min (St[lson].val, St[rson].val);
    St[x].tol = St[lson].tol * (St[x].val == St[lson].val) + St[rson].tol * (St[x].val == St[rson].val);
}
void Modify (int x, int l, int r, int key) {
    if (St[x].l >= l && St[x].r <= r) {
        St[x].val += key, St[x].add += key;
        return;
    }
    push (x);
    int mid = (St[x].l + St[x].r) >> 1;
    if (mid >= l) Modify (lson, l, r, key);
    if (mid < r) Modify (rson, l, r, key);
    update (x);
}
void Query (int x, int l, int r) {
    if (St[x].l >= l && St[x].r <= r) {
        ans += St[x].tol * (St[x].val == 1);
        return;
    }
    push (x);
    int mid = (St[x].l + St[x].r) >> 1;
    if (mid >= l) Query (lson, l, r);
    if (mid < r) Query (rson, l, r);
}
int main() {
    scanf ("%d", &n);
    for (int i = 1, x; i <= n; i++) {
        scanf ("%d", &x);
        p[x] = i;
    }
    Build (1, 1, n);
    for (int i = n; i; i--) {
        a[p[i]] = i;
        int x = a[p[i] - 1], y = a[p[i] + 1];
        if (x > y) swap (x, y);
        if (x && y) Modify (1, i, x - 1, 1), Modify (1, y, n, -1);
        else if (y) Modify (1, i, y - 1, 1);
        else Modify (1, i, n, 1);
        Query (1, i, n);
    }
    printf ("%d", ans);
}
View Code

 

CF的代码:

#include <iostream>
#include <cstdio>
using namespace std;
#define lson x<<1
#define rson x<< 1 | 1
const int MAXN = 300009;
struct Segtree {
    int l, r, val, add, tol, tol2;
} St[MAXN << 2];
int p[MAXN], a[MAXN];
int n;
long long ans;
void Build (int x, int l, int r) {
    St[x].l = l, St[x].r = r;
    St[x].tol = r - l + 1;
    if (l == r) return;
    int mid = (l + r) >> 1;
    Build (lson, l, mid), Build (rson, mid + 1, r);
}
void push (int x) {
    if (St[x].add == 0) return;
    St[lson].add +=St[x].add, St[rson].add += St[x].add;
    St[lson].val += St[x].add, St[rson].val += St[x].add;
    St[x].add = 0;
}
void update (int x) {
    St[x].val = min (St[lson].val, St[rson].val);
    St[x].tol = St[lson].tol * (St[x].val == St[lson].val) + St[rson].tol * (St[x].val == St[rson].val);
    St[x].tol2=St[lson].tol*(St[x].val+1==St[lson].val)+St[rson].tol*(St[x].val+1==St[rson].val);
    St[x].tol2+=St[lson].tol2*(St[x].val==St[lson].val)+St[rson].tol2*(St[x].val==St[rson].val);
}
void Modify (int x, int l, int r, int key) {
    if (St[x].l >= l && St[x].r <= r) {
        St[x].val += key, St[x].add += key;
        return;
    }
    push (x);
    int mid = (St[x].l + St[x].r) >> 1;
    if (mid >= l) Modify (lson, l, r, key);
    if (mid < r) Modify (rson, l, r, key);
    update (x);
}
void Query (int x, int l, int r) {
    if (St[x].l >= l && St[x].r <= r) {
        ans += St[x].tol * (St[x].val <=2)+St[x].tol2*(St[x].val==1);
        return;
    }
    push (x);
    int mid = (St[x].l + St[x].r) >> 1;
    if (mid >= l) Query (lson, l, r);
    if (mid < r) Query (rson, l, r);
}
int main() {
    scanf ("%d", &n);
    for (int i = 1, x; i <= n; i++) {
        scanf ("%d", &x);
        p[x] = i;
    }
    Build (1, 1, n);
    for (int i = n; i; i--) {
        a[p[i]] = i;
        int x = a[p[i] - 1], y = a[p[i] + 1];
        if (x > y) swap (x, y);
        if (x) Modify (1, i, x - 1, 1), Modify (1, y, n, -1);
        else if (y) Modify (1, i, y - 1, 1);
        else Modify (1, i, n, 1);
        Query (1, i, n);
    }
    printf ("%I64d", ans-n);
}
View Code

 

转载于:https://www.cnblogs.com/keam37/p/4335914.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值