这两题一样就放一起写了。
无非就是值周的数据范围大一些。。
校门外的树可以直接暴力就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; }