CF 1132 problem_C:Painting the Fence(暴力枚举+差分处理)

题意是给出一段长为n的序列,给出q个连续子序列,让你选择其中q-2个子序列,求覆盖的最大范围是多大。

一开始想贪心,每次选最长的,然后判断一下覆盖相交的情况。但题目没有要求你覆盖的范围一定是连续的,不要求连续的话子序列选择情况和判断比较复杂,所以贪心思路就直接丢弃了。一看只要选q-2个,2个不选,那就枚举哪两个2不选。

然后就被怎么处理卡住了,首先想到用差分算出每一个点被覆盖多少次,然后枚举两个子序列,继续差分扣去这一段的覆盖次数,然后遍历看有多少个点被覆盖次数大于1,找最大值就是答案。不过这样搞是要o(n^3)复杂度的,显然会炸。然后硬是没想到怎么处理。

实际上枚举的那两个子序列,只会影响子序列区间内覆盖次数为1点,那么计算子序列内有多少个覆盖次数仅为1的点,扣掉后就是实际覆盖长度。

所以可以再搞一个数组d,如果一个点i覆盖次数为1,则di=1,否则di=0;利用前缀和思想,可以计算出[L,R]中有多少个覆盖次数仅为1的点。

具体操作为:在第一次做差分时,就先枚举一个子序列,该子序列不做差分。这样先得到q-1个序列覆盖点总个数。得到差分序列后可以得到一个d数组,枚举第二个子序列,计算区间内覆盖次数为1的点的个数,扣去就等于不选第二个子序列覆盖的点的总个数,依次筛选得到最大值即为答案。

#include<bits/stdc++.h>
using namespace std;
int L[5010],R[5010];
int vis[5010];
int main(){
 	int n,q;
 	scanf("%d%d",&n,&q);
 	for(int i = 1; i <= q; i++)
  		scanf("%d%d",&L[i],&R[i]);
 	int ans=0,res=0;
 	for(int i = 1; i <= q; i++){
  		memset(vis,0,sizeof(vis));
  		int temp=0;
  		for(int j = 1; j <= q; j++){
   			if(j==i) continue;
   			vis[L[j]]++;vis[R[j]+1]--;
  		}
  		for(int j = 1; j <= n; j++){
   			vis[j]+=vis[j-1];
   			if(vis[j]) temp++;
  		}
  		for(int j = 1; j <= n; j++)
   			if(vis[j]!=1) vis[j]=0;
  		for(int j = 1; j <= n; j++) 
   			vis[j]+=vis[j-1];  
  		for(int j = 1; j <= q; j++){
   			if(i==j) continue;
   			res=temp-(vis[R[j]]-vis[L[j]-1]);
   			ans=max(ans,res);
  		}
 	}
 	printf("%d",ans);
} 

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/88545640