这两题一样就放一起写了。
无非就是值周的数据范围大一些。。

校门外的树可以直接暴力就ok。

值周的话。
考虑把区间修改变成区间端点修改,即差分,m次结束后进行一次前缀和。然后统计没有被标记过的即可。

这里顺便提一下扩展的内容,就是说L很大,数组开不下,m的大小还是不变。

这里我们不能差分了,应该数组太大了开不下。那么我们可以反过来求,求有多少个被覆盖的,总长度减去被覆盖的就是答案。

那么问题就是怎么求被覆盖的点。

对m个段进行排序,以左端点升序
我们维护一个当前的最大的右端点r,为什么?因为区间的点可能会被多次覆盖。
所以每到一个新的区间段,首先看这个区间的左端点是不是大于前面维护的最大的右端点r,是的话,说明当前区间段不和前面的覆盖,那么自然要加上当前段的区间长度,更新右端点。
如果当前段的左端点小于前面维护的右端点最大值r呢?继续看当前段的右端点是不是大于r,是的话多覆盖的区间就是当前右端点-r,并且更新r即可。

差分、前缀和

#include<bits/stdc++.h>
using namespace std;
int a[100000005];
int main(){
    ios::sync_with_stdio(0);
    int n,m;cin>>n>>m;
    while(m--){
        int l,r;cin>>l>>r;
        a[l]++,a[r+1]--;
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        a[i]+=a[i-1];
        if(!a[i]) ans++;
    }
    cout<<ans+!a[0]<<endl;
    return 0;
}

排序维护右端点

#include<bits/stdc++.h>
using namespace std;
pair<int,int> c[1000005];
int main(){
    int n,m;cin>>n>>m;
    for(int i=1;i<=m;i++){
        int l,r;cin>>l>>r;
        c[i]={l,r};
    }
    sort(c+1,c+1+m);
    int ans=0,r=0;
    for(int i=1;i<=m;i++){
        if(c[i].first>=r){
            ans+=c[i].second-c[i].first+1;
            r=c[i].second;
        }
        else if(c[i].second>=r){
            ans+=c[i].second-r;
            r=c[i].second;
        }
    }
    cout<<n+1-ans;
    return 0;
}