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

来源:互联网 发布:电视选购 知乎 编辑:程序博客网 时间:2024/06/16 21:01
题目:
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];

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

原创粉丝点击