题目描述:
给你m个位于[1,n]的区间p,现在有长度为K的区间b和c。设对于区间$p[i]$,定义$a[i]$为$p[i]$分别与b,c相交长度的较大值,现在问区间b和c位于何处时,$sum p_{i}$最大,输出这个最大值,n,m,K<=2000
好题目,dkr yyds!
暴力怎么搞?暴力枚举b,c的位置,然后O(n)计算,总时间$O(n^{3})$
考虑优化这个暴力,每次只暴力枚举b的位置,并统计所有pi与b相交的长度之和。如果c和pi相交比b大,那么会产生额外贡献,这个额外贡献的值就是c与pi相交长度减掉b与pi相交的长度
考虑每次都把c区间从左到右一格一格挪,挪进第i个区间的长度超过ai时,开始产生贡献,每超过一格,贡献就+1。每挪一次,我们就在c右端点的位置打上+1标记
然而挪多了可能不再增加新贡献,就停止打+1。
挪出去了还会减少贡献,开始打-1,直到贡献减成0,停止打-1
实际操作中也不必真的挪c,也不用记录什么时候减到0,推位置就可以了
怎么推位置呢?分K和xi长度大小讨论,推吧推吧就出来了,细节比较多
+1和-1这些标记都是连续的,差分就行。那么求两次前缀和即可
总时间$O(n^{2})$
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define N1 2005 5 #define ll long long 6 #define mod 998244353 7 using namespace std; 8 9 int n,m,K; 10 int l[N1],r[N1],a[N1]; 11 int adds[N1],addt[N1],subs[N1],subt[N1]; 12 ll sum1[N1],sum2[N1]; 13 14 void clr() 15 { 16 memset(adds,0,sizeof(adds)); 17 memset(addt,0,sizeof(addt)); 18 memset(subs,0,sizeof(subs)); 19 memset(subt,0,sizeof(subt)); 20 memset(sum1,0,sizeof(sum1)); 21 memset(sum2,0,sizeof(sum2)); 22 } 23 24 int main() 25 { 26 freopen("a.txt","r",stdin); 27 scanf("%d%d%d",&n,&m,&K); 28 for(int i=1;i<=m;i++) scanf("%d%d",&l[i],&r[i]); 29 ll ANS=0,ans=0; 30 for(int i=1;i+K-1<=n;i++)//l~l+K-1 31 { 32 int L=i,R=i+K-1; ans=0; 33 for(int j=1;j<=m;j++) 34 { 35 a[j]=max(0,min(r[j],i+K-1)-max(l[j],i)+1); 36 ans+=a[j]; 37 } 38 for(int j=1;j<=m;j++) 39 { 40 ++adds[l[j]+a[j]]; 41 ++addt[min(r[j]+1,min(l[j]+K,n+1))]; //addt: +1的结束位置 +1 42 ++subs[max(min(l[j]+K,n+1),r[j]+1)]; //subs: -1start_point 43 ++subt[min(r[j]-a[j]+1+K,n+1)]; //subt: -1end_point +1 44 } 45 ll tmp=0; 46 for(int j=1;j<=n;j++) sum1[j]+=sum1[j-1]+adds[j]-addt[j]-subs[j]+subt[j]; 47 for(int j=1;j<=n;j++) sum2[j]=sum2[j-1]+sum1[j], tmp=max(tmp,sum2[j]); 48 ANS=max(ANS,ans+tmp); 49 clr(); 50 } 51 printf("%lld ",ANS); 52 return 0; 53 }