【IOI2020国家集训队作业 Part 1】CF526F Pudding Monsters

题目

题目描述
In this problem you will meet the simplified model of game Pudding Monsters.

An important process in developing any game is creating levels. A game field in Pudding Monsters is an n×nn×n rectangular grid, nn of its cells contain monsters and some other cells contain game objects. The gameplay is about moving the monsters around the field. When two monsters are touching each other, they glue together into a single big one (as they are from pudding, remember?).

Statistics showed that the most interesting maps appear if initially each row and each column contains exactly one monster and the rest of map specifics is set up by the correct positioning of the other game objects.

A technique that’s widely used to make the development process more efficient is reusing the available resources. For example, if there is a large n×nn×n map, you can choose in it a smaller k×kk×k square part, containing exactly kk monsters and suggest it as a simplified version of the original map.

You wonder how many ways there are to choose in the initial map a k×kk×k ( 1<=k<=n1<=k<=n ) square fragment, containing exactly kk pudding monsters. Calculate this number.

输入格式
The first line contains a single integer nn ( 1<=n<=3×10^{5}1<=n<=3×10
5
) — the size of the initial field.

Next nn lines contain the coordinates of the cells initially containing monsters. The ii -th of the next lines contains two numbers r_{i},c_{i}r
i

,c
i

( 1<=r_{i},c_{i}<=n1<=r
i

,c
i

<=n ) — the row number and the column number of the cell that initially contains the ii -th monster.

It is guaranteed that all r_{i}r
i

are distinct numbers and all c_{i}c
i

are distinct numbers.

输出格式
Print the number of distinct square fragments of the original field that can form a new map.

题意翻译
给定一个 n \times nn×n 的棋盘,其中有 nn 个棋子,每行每列恰好有一个棋子。
求有多少个 k \times kk×k 的子棋盘中恰好有 kk 个棋子。
n \le 3 \times 10^5n≤3×10
5

输入输出样例
输入 #1复制
5
1 1
4 3
3 2
2 4
5 5
输出 #1复制
10

思路

首先把它拍扁到一个序列上应该不难想到,然后就是求满足 max ⁡ [ l , r ] − min ⁡ [ l , r ] = r − l \max[l,r]-\min[l,r]=r-l max[l,r]min[l,r]=rl [ l , r ] [l,r] [l,r] 个数,其中 max ⁡ [ l , r ] \max[l,r] max[l,r] 表示区间中的最大值。
套路地枚举 r r r
我们尝试维护 max ⁡ [ l , r ] − min ⁡ [ l , r ] + l \max[l,r]-\min[l,r]+l max[l,r]min[l,r]+l,然后我们需要支持有多少个这个式子等于 r r r
这个时候你可以秒出一个算法:用树状数组去维护差分,开两个单调栈,然后退栈就做一个区间修改……
然后就假掉了,因为退栈的时候有可能得遍历一遍另一个栈,复杂度直接被卡成平方带一个 log 比暴力还劣
既然不能直接维护出现次数,我们不妨用线段树去维护在 [ 1 , r ] [1,r] [1,r] 中有多少个 l l l 满足 max ⁡ [ l , r ] − min ⁡ [ l , r ] + l = r \max[l,r]-\min[l,r]+l=r max[l,r]min[l,r]+l=r,利用单调栈,我们需要的就是区间加和改变等式的右边……
然后就又不会了。
其实我们距离正解只有一步之遥。我们发现 max ⁡ [ l , r ] − min ⁡ [ l , r ] ≥ r − l \max[l,r]-\min[l,r]\ge r-l max[l,r]min[l,r]rl,所以我们维护 max ⁡ [ l , r ] − min ⁡ [ l , r ] + l \max[l,r]-\min[l,r]+l max[l,r]min[l,r]+l 的最小值及出现次数即可。

代码

#include<bits/stdc++.h>
#define ll long long
#define ls p<<1
#define rs p<<1|1
#define md ((l+r)>>1)
using namespace std;
const int N = 3e5 + 7;
int n,a[N],sx[N],tx,sn[N],tn;
map <int,int> p;
struct T 
{
    int l,r,x,c,z;
} t[N<<2];
ll ans;

void build(int p,int l,int r)
{
    t[p].l = l,t[p].r = r,t[p].c = r - l + 1;
    if(l == r) return;
    build(ls,l,md);
	build(rs,md + 1,r);
}

void add(int p,int x) 
{
    t[p].x += x,t[p].z += x;
}

void upd(int p,int l,int r,int x) {
    if(t[p].l >= l && t[p].r <= r) return add(p,x);
    if(t[p].z) add(ls,t[p].z),add(rs,t[p].z),t[p].z = 0;
    if(l <= md) upd(ls,l,r,x);
    if(r > md) upd(rs,l,r,x);
    t[p].x = min(t[ls].x,t[rs].x);
    t[p].c = (t[ls].x == t[p].x ? t[ls].c : 0) + (t[rs].x == t[p].x ? t[rs].c : 0);
}

int main() {
    scanf("%d",&n);
    for(int i = 1,x,y; i <= n; i++) scanf("%d%d",&x,&y),a[x] = y;
    build(1,1,n);
    for(int i = 1; i <= n; i++) {
        while(tx && a[sx[tx]] < a[i]) upd(1,sx[tx-1] + 1,sx[tx],-a[sx[tx]]),--tx;
        while(tn && a[sn[tn]] > a[i]) upd(1,sn[tn-1] + 1,sn[tn],a[sn[tn]]),--tn;
        upd(1,p[a[i]] + 1,i,-1),p[a[i]] = sx[++tx] = sn[++tn] = i;
        upd(1,sx[tx-1] + 1,i,a[i]),upd(1,sn[tn-1] + 1,i,-a[i]);
        ans += t[1].c;
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值