【尺取法】swpu涨姿势之区间刷新

题目:
value_Dragon是一个有钱人,快过年了,所以他准备发红包,但是他发红包的方式很奇葩,
他让n个人排成一排,每次选择1-n中的一段区间[l,r]发,给区间中的每一个人一块钱,
就这样发了m次红包,发完后他想知道在[l,r]的子区间中有多少个区间满足以下要求:
1.这个区间得到钱的总数不少于s;
2.这个区间可以被分成两个不想交的子区间且每个子区间得到的钱的总数不小于w;
(注:一个区间的子区间包括自己本身)
防坑提醒:长度为1的区间比如[1,1]是不能被分成两个子区间的

输入:
第一行是一个整数T代表数据的组数;
接下来有T组数据
每组数据开头有四个整数,分别代表n m s w;
接下来m行,每行是两个数l,r代表区间[l,r]的左右端点;
其中T<=10;
n<=10^6,m<=10^5;
0<l<=r<=n;
0<=w<=s<10^8;

输出:
对于每组数据输出一行,代表符合要求的区间个数;

样例输入:
4
1 0 0 0

1000000 0 0 0

1000000 1 0 0
1 1000000

10 10 20 14
2 10
5 9
5 5 
6 8
2 6
9 10
6 7
6 10
4 5
5 7

样例输出:
0
499999500000
499999500000

8


思路:运用双指针,左右指针均初始化为1,先移动右指针,再移动左指针,得到一个符合条件的最小区间,最后计算答案是ans+=l,而不是ans+=1;那是因为现在这个l-r的区间是满足条件的最小区间,那么再加上l前的数,一定也满足,所以是ans+=l;

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn = 1e6+10;

int t,n,m,l,r,w,s,x,y;
int a[maxn],sum[maxn];

int main(){
    scanf("%d",&t);
    while(t--){
        memset(a,0,sizeof(a));
        memset(sum,0,sizeof(sum));
        scanf("%d%d%d%d",&n,&m,&s,&w);
        while(m--){
            scanf("%d%d",&x,&y);
            a[x]++;a[y+1]--;    //小技巧
        }
        for(int i = 1;i <= n;i++)
            a[i] = a[i-1] + a[i];
        for(int i  = 1;i <= n;i++)
            sum[i] = sum[i-1] + a[i];
        ll ans = 0,l = 1,r = 1;
        for(int i = 1;i <= n;i++){                //得到一个满足条件的最小区间!
            while(r < i&&sum[i] - sum[r] >= w)
                ++r;
            while(l < r-1&&sum[i] - sum[l] >= s&&
                  sum[r-1]-sum[l] >= w)
                ++l;
            if(l < r&&sum[i]-sum[l-1] >= s&&sum[i]-
               sum[r-1] >= w&&sum[r-1]-sum[l-1]>=w)
                ans += l;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

小技巧:

m次发红包,每次都要对给定的区间l-r进行刷新+1?当然不用这么繁琐的操作,使用下面的小技巧:

代码:

while(m--){
            scanf("%d%d",&x,&y);
            a[x]++;a[y+1]--;    //小技巧
        }
        for(int i = 1;i <= n;i++)
            a[i] = a[i-1] + a[i];

尺取法视频链接: 点击打开链接

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值