CF1956F Nene and the Passing Game 解题报告

假设 \(j>i\) ,则:\(i+l_i\le j-l_j,i+r_i\le j-r_j\)

所以相当于看区间 \([i+l_i,i+r_i]\) 和区间 \([j-r_j,j-l_j]\) 是否有交集

可以将这些区间放在数轴上,考虑建虚点,将数轴上的每个点向包含它的区间连边

但是这样会有一个问题,记加为右区间,减为左区间,此时就无法判断是哪种区间在相交

所以可以只保留既有左区间又有右区间覆盖的点,这样就能保证一定是符合条件的

但是依次对每个点连边显然会T,考虑优化

先用 \(pre\)\(ne\) 记录前/后满足条件的最近的点

对于区间 \([l,r]\),操作就应在 \([ne_l,pre_r]\)

利用差分的思想,统计数轴上每个点被覆盖且不为末端点的次数

因为作为末端点,后面没有与之相邻的点,就无法向后合并

如果次数不为0,就向后连边,最后看有多少连通块即可

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int T,n,l[2000006],r[2000005],pre[2000005],ne[2000006];
int cntl[2000006],cntr[2000006],f[4000006],tot;
int v[2000005];
int find(int x)
{
	if(f[x]!=x) f[x]=find(f[x]);
	return f[x];
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		tot=0;
		for(int i=0;i<=n;i++)
		{
			cntl[i]=cntr[i]=0;
			pre[i]=ne[i]=v[i]=0;
		}
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d",&l[i],&r[i]);
			cntl[max(1,i-r[i])]++;
			cntl[max(1,i-l[i]+1)]--;
			cntr[min(n,i+l[i])]++;
			cntr[min(n,i+r[i]+1)]--;
			f[i]=i;
		}
		for(int i=1;i<=n;i++)
		{
			cntl[i]+=cntl[i-1];
			cntr[i]+=cntr[i-1];
			if(cntl[i]&&cntr[i])
			{
				pre[i]=ne[i]=++tot;
				f[n+tot]=tot+n;
			}
			
			else
			{
				pre[i]=pre[i-1];
			}
		}
		ne[n+1]=1e9;
		for(int i=n;i>0;i--)
		{
			if(!ne[i]) ne[i]=ne[i+1];
		}
		for(int i=1;i<=n;i++)
		{
			int ql=ne[max(1,i-r[i])];
			int qr=pre[max(0,i-l[i])];
			if(ql<=qr)
			{
				v[ql]++,v[qr]--;
				f[find(i)]=find(qr+n);
			}
			ql=ne[min(n+1,i+l[i])];
			qr=pre[min(n,i+r[i])];
			if(ql<=qr)
			{
				v[ql]++,v[qr]--;
				f[find(i)]=find(qr+n);
			}
//			printf("%d",i);
		}
		for(int i=1;i<=tot;i++)
		{
			v[i]+=v[i-1];
		}
		for(int i=1;i<=tot;i++)
		{
			if(v[i])
			{
				f[find(i+n)]=find(n+i+1);
			}
		}
		int ans=0;
		for(int i=1;i<=n+tot;i++)
		{
			if(f[i]==i) ans++;
		}
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2025-01-15 08:34  wangsiqi2010916  阅读(36)  评论(0)    收藏  举报