【CF1000C】Covered Points Count(离散化+差分)

点此看题面

大致题意: 给出(n)条线段,分别求有多少点被覆盖(1)次、(2)次...(n)次。

正常的算法

好吧,这道题目确实有个很简单的贪心做法(只可惜我做的时候没有想到,结果想了半天只想出一个无比麻烦的),这里介绍一个稍微有些复杂的。

不正常的算法(我的算法)

考虑离散化每一个出现过的点以及这些点后面的点(之所以要离散化这些后面的点,是为了方便后面的差分)。

假如我们用(p[i])来表示原来为(i)的数离散化后的值,并用(q[i])表示离散化后值为(i)的数原来的值,则:

对于一条线段,假设它的左端点为(l),右端点为(r),那么我们就将(f[p[l]])加1,并将(f[p[r+1]])减1(经典的差分套路),最后求出其前缀和,就可以求出每一块被覆盖的次数,从而将其还原计算出答案。

代码

#include<bits/stdc++.h>
#define max(x,y) (x>y?x:y)
#define min(x,y) (x<y?x:y)
#define LL long long
#define N 200000
using namespace std;
int n;
LL x[N+5],y[N+5],z[4*N+5],f[4*N+5],ans[N+5];
map<LL,LL> p,q;
LL read()
{
	LL x=0,f=1;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if(ch=='-') f=-1,ch=getchar();
	while(ch>='0'&&ch<='9') (x*=10)+=ch-'0',ch=getchar();
	return x*=f;
}
void write(LL x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++) z[4*i-3]=x[i]=read(),z[4*i-2]=x[i]+1,z[4*i-1]=y[i]=read(),z[4*i]=y[i]+1;
	int w=4*n,k=0;
	sort(z+1,z+w+1);
	for(int i=1;i<=w;i++) if(z[i]!=z[i-1]||i==1) p[z[i]]=i-k,q[i-k]=z[i];else k++;//离散化
	for(int i=1;i<=n;i++) f[p[x[i]]]++,f[p[y[i]]+1]--;//差分
	for(int i=1;i<w;i++) ans[f[i]+=f[i-1]]+=q[i+1]-q[i];//前缀和累加,然后还原计算答案
	for(int i=1;i<=n;i++) write(ans[i]),putchar(' ');
	return 0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF1000C.html