題目描述:
給你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\n",ANS); 52 return 0; 53 }