题意是给出一段长为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);
}