codeforce1000C 求线段覆盖次数特殊操作及其拓展

通过标记起点,使其+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('\n')
#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':' ');
}

  

 

这样的一个思想可以拓展,用于解决一些类似的区间覆盖问题。

 

拓展一:

区间染色问题,很简单,再增加一个数组记录颜色即可。

 

拓展二:

二维区间(平面覆盖)

如果把这道题的问题规模扩充到一个平面,要如何处理?

直接得出一个不同规模的解有些难度,让我们先从已有结论出发:

标记一个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('\n')
#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\n",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

posted @ 2018-07-13 17:22  诚信肥宅  阅读(324)  评论(0编辑  收藏  举报