通过标记起点,使其+1,标记终点后第一个的点,使其-1,然后从头到尾遍历一次,进行类似于前缀和的运算,每次取点的值,当前值为n,表示被n条线段覆盖的区域增加一个
图解示例:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<stack> #include<map> #include<vector> #include<queue> #include<set> #include<iomanip> #include<cctype> using namespace std; const int MAXN=2e5+5; const int INF=1<<30; const long long mod=1e9+7; const double eps=1e-8; #define ll long long #define edl putchar(' ') #define sscc ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define FOR(i,a,b) for(int i=a;i<=b;i++) #define ROF(i,a,b) for(int i=a;i>=b;i--) #define FORLL(i,a,b) for(ll i=a;i<=b;i++) #define ROFLL(i,a,b) for(ll i=a;i>=b;i--) #define mst(a) memset(a,0,sizeof(a)) #define mstn(a,n) memset(a,n,sizeof(a)) #define zero(x)(((x)>0?(x):-(x))<eps) int n,cnt; ll ans[MAXN],las; struct num { ll l,r; }a[MAXN]; map<ll,int> mp; int main() { scanf("%d",&n); FOR(i,1,n) scanf("%lld%lld",&a[i].l,&a[i].r); FOR(i,1,n) { mp[a[i].l]++,mp[a[i].r+1]--; } map<ll,int>::iterator it; for(it=mp.begin();it!=mp.end();it++) { if(it==mp.begin()) { las=it->first; cnt=0; } ans[cnt]+=it->first-las; cnt+=it->second; las=it->first; } FOR(i,1,n) printf("%lld%c",ans[i],i==n?' ':' '); }
这样的一个思想可以拓展,用于解决一些类似的区间覆盖问题。
拓展一:
区间染色问题,很简单,再增加一个数组记录颜色即可。
拓展二:
二维区间(平面覆盖)
如果把这道题的问题规模扩充到一个平面,要如何处理?
直接得出一个不同规模的解有些难度,让我们先从已有结论出发:
标记一个n*m的区间相当于标记长度为m的区间n次,这样一次操作的复杂度是O(n)或O(m)而不是O(1)
示例:
我们这样标记就一定要先从左向右处理,然后才能换行,因为这种做法的本质是把平面降成了线,所以要遵循一维的规则(红色区域为覆盖域)。
注意到一维的规则里,只有上一次的状态(左侧)会对现在的结果产生影响
因此猜测二维情况下同时与左侧与上侧相关:
1.处于左边界时,左侧无关联,所以上侧的值要计入当前
2.处于上边界时,上侧无关联,所以左侧的值要计入当前
3.处于非边界时,如果同时计入左侧和上侧,会导致值比预期的要大,但如果同时存在上侧和左侧,则左上侧一定存在,减去左上侧的值,那么计数即符合预期。
得出类似前缀和的算式:(aij)+=(ai-1j)+(aij-1)-(ai-1j-1)
通过对算式的推演,发现正确的赋值方式如下:
及其计算的结果:
符合预期情况。
牛客网暑期ACM多校训练营(第二场)J题可以用该方法解决
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<stack> #include<map> #include<vector> #include<queue> #include<set> #include<iomanip> #include<cctype> #include<stack> #include<ctime> using namespace std; const int MAXN=1e6+5; const int INF=1<<30; const long long mod=1e9+7; const double eps=1e-8; typedef long long ll; #define edl putchar(' ') #define sscc ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define FOR(i,a,b) for(int i=a;i<=b;i++) #define ROF(i,a,b) for(int i=a;i>=b;i--) #define FORLL(i,a,b) for(ll i=a;i<=b;i++) #define ROFLL(i,a,b) for(ll i=a;i>=b;i--) #define mst(a) memset(a,0,sizeof(a)) #define mstn(a,n) memset(a,n,sizeof(a)) #define zero(x)(((x)>0?(x):-(x))<eps) vector<ll> color[MAXN]; vector<int> now[MAXN],mp[MAXN]; int main() { int n,m,k,ans; while(scanf("%d%d%d",&n,&m,&k)!=EOF) { ans=0; FOR(i,0,n+1) { mp[i].resize(m+5); now[i].resize(m+5); color[i].resize(m+5); } FOR(i,1,n) FOR(j,1,m) { scanf("%d",&mp[i][j]); color[i][j]=0; now[i][j]=0; } int x1,y1,x2,y2; ll t; while(k--) { scanf("%d%d%d%d%lld",&x1,&y1,&x2,&y2,&t); color[x1][y1]+=t; color[x1][y2+1]-=t; color[x2+1][y1]-=t; color[x2+1][y2+1]+=t; now[x1][y1]++; now[x1][y2+1]--; now[x2+1][y1]--; now[x2+1][y2+1]++; } FOR(i,1,n) { FOR(j,1,m) { now[i][j]+=now[i-1][j]+now[i][j-1]-now[i-1][j-1]; color[i][j]+=color[i-1][j]+color[i][j-1]-color[i-1][j-1]; if(now[i][j]>1) ans++; if(now[i][j]==1&&color[i][j]!=mp[i][j])ans++; } } printf("%d ",ans); } return 0; }
再扩展到三维空间:aijk+=(ai-1jk)+(aij-1k)+(aijk-1)-(ai-1j-1k)-(ai-1jk-1)-(aij-1k-1)+(ai-1j-1k-1)
赋值ax1y1z1=1,a(x2+1)(y2+1)(z2+1)=1,a其余顶点为-1