CodeForces1132 C.Painting the Fence (前缀和+枚举)

C.Painting the Fence

You have a long fence which consists of n sections. Unfortunately, it is not painted, so you decided to hire q painters to paint it. i-th painter will paint all sections x such that li≤x≤ri.

Unfortunately, you are on a tight budget, so you may hire only q−2 painters. Obviously, only painters you hire will do their work.

You want to maximize the number of painted sections if you choose q−2 painters optimally. A section is considered painted if at least one painter paints it.

Input

The first line contains two integers n and q (3≤n,q≤5000) — the number of sections and the number of painters availible for hire, respectively.

Then q lines follow, each describing one of the painters: i-th line contains two integers li and ri (1≤li≤ri≤n).

Output

Print one integer — maximum number of painted sections if you hire q−2 painters.

Examples
Input

7 5
1 4
4 5
5 6
6 7
3 5

Output

7

Input

4 3
1 1
2 2
3 4

Output

2

Input

4 4
1 1
2 2
2 3
3 4

Output

3

思路:

题目数据5e3可以O(n2)枚举,但是如果暴力删除和恢复就O(n3)了,肯定不行。

显然只有覆盖次数为1或者为2的地方可能因为删除而改变总覆盖数量

可以用前缀和O(1)得出删除的答案
one[i]表示1-i中被覆盖两次的次数
sec[i]表示1-i中被覆盖两次的次数
利用这样的前缀和就可以马上的出区间内被覆盖次数为1或者2的个数

先计算全选的总覆盖数量
枚举不选择的两个人,减去两个人区间并集的覆盖次数为1的个数,减去两个人区间交集的覆盖次数为2的个数,然后就得到不选这两个人的总覆盖数量,每次枚举更新答案就行了。

code:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxm=5e3+5;
int cnt[maxm];//cnt[i]为i的被覆盖次数
int one[maxm];//覆盖一次的次数前缀和
int sec[maxm];//覆盖两次的次数前缀和
int l[maxm],r[maxm];
int n,q;
signed main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=q;i++){
        scanf("%d%d",&l[i],&r[i]);
        cnt[l[i]]++;//差分优化
        cnt[r[i]+1]--;
    }
    int temp=0;
    for(int i=1;i<=n;i++){
        cnt[i]+=cnt[i-1];//前缀和恢复差分
        if(cnt[i])temp++;//覆盖至少一次答案增加
        one[i]=one[i-1]+(cnt[i]==1);//1-i中覆盖一次的个数
        sec[i]=sec[i-1]+(cnt[i]==2);//1-i中覆盖两次的个数
    }
    int ans=0;
    for(int i=1;i<=q;i++){
        for(int j=i+1;j<=q;j++){
            int t=temp;
            if(r[i]<l[j]||r[j]<l[i]){//线段不相交,减去两个区间覆盖一次的个数
                t-=one[r[i]]-one[l[i]-1];
                t-=one[r[j]]-one[l[j]-1];
            }else{//线段相交:减去并区间覆盖一次的个数,减去交区间覆盖两次的个数
                t-=one[max(r[i],r[j])]-one[min(l[i],l[j])-1];
                t-=sec[min(r[i],r[j])]-sec[max(l[i],l[j])-1];
            }
            ans=max(ans,t);
        }
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值