暑 假 隊 測 Round #3
阿新 • • 發佈:2020-08-08
\(100+20+0=120pts\)
T1:單調佇列優化dp
線性dp,設\(f[i]\)為前\(i\)個數的最小和(\(i\)被選擇)。
顯然:
\(f[i]=min\){\(~~f[j]+a[i]~~\)}\(~~(i>j>i-m)~~\),
拆一下:
\(f[i]=min\){\(~f[j]~\)}\(~\)\(+\)\(~\)\(a[i]\)\(~~(i>j>i-m)~~\),
已經很裸了,單調佇列維護最小的\(f[j]\).
\(T1のCode\):
#include<bits/stdc++.h> using namespace std; const int N=2e5+10; int n,m,a[N],f[N],mn=INT_MAX,q[N]; int main(){ //freopen("minn.in","r",stdin); //freopen("minn.out","w",stdout); memset(f,0x3f,sizeof(f)); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); f[0]=0; int l=1,r=1;q[1]=0; for(int i=1;i<=n;i++){ while(l<=r&&q[l]<i-m)l++; f[i]=f[q[l]]+a[i]; while(l<=r&&f[q[r]]>=f[i])r--; q[++r]=i; } for(int i=n;i>=n-m+1;i--)mn=min(mn,f[i]); printf("%d",mn); return 0; }
T2:區間dp
設\(f[l][r]\)為\(l\) ~ \(r\)中的合法序列方案數,
根據空串序列合法性,所以\(f[i][i-1]=1\),
最後答案是\(f[1][n]\)。
很容易想到\(f[l][r]\)\(=\)\(\sum{f[l][k]+f[k+1][r]}\),
其實這個轉移方程是錯誤的,看這組樣例:
( ) ( ) ( )
這個樣例只有唯一的合法括號序列,可是在上述轉移中會出現重複計數。
所以在合併時,如有\(A\)和\(B\)兩個合法括號序列,我們應保證是形如\(A\)\(~\)\(~\)\((\)\(~\)\(B\)\(~\)\()\)
所以有
- \(f[l][r]+=f[l][k]*f[k+2][r-1]~(~s[k+1]==s[r]~~||~~s[k+1]==?~~||~~s[r]==?)\)
- \(f[l][r]+=f[l][k]*f[k+2][r-1]*3~~(~~s[k+1]==s[r]==?~~)~~\)
\(T2のCode\):
#include<bits/stdc++.h> #define ll long long using namespace std; const int N=205; const int mod=1e5; int n; ll f[N][N]; char s[N]; char ls[3]= {'(','[','{'}; char rs[3]= {')',']','}'}; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) cin>>s[i]; for(int i=1;i<=n;i++)f[i][i-1]=1; for(int len=2;len<=n;len+=2) for(int l=1;l<=n-len+1;l++){ int r=l+len-1; for(int k=l-1;k<=r;k++) for(int t=0;t<3;t++) if((s[k+1]==ls[t]||s[k+1]=='?')&&(s[r]=='?'||s[r]==rs[t])) f[l][r]=(f[l][r]+((f[l][k]*f[k+2][r-1])%mod))%mod; } printf("%lld",f[1][n]%mod); return 0; }
T3:單調佇列
其實是最水的一道。。可惜保齡收場。
仔細觀察發現解法複雜度與\(n<=10^9\)的驚人範圍無關。
\(m<=5\times10^3\),可以搞一個\(O(m^2)\)
思考:一個行李箱通過所有障礙的下限是什麼?
當然就是通過最狹窄的地方!
想到這個,我們可以列舉長度,然後單調佇列跑一遍,得到寬度,然後計算面積即可。
T3のCode:
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
const int N=5e3+10;
using namespace std;
int n,m,a[N],b[N],q1[N],q2[N];
ll ans;
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)scanf("%d",&a[i]);
for(int i=1;i<=m;i++)scanf("%d",&b[i]);
for(int k=1;k<=m;k++){
int d=k,mn=inf;
int l1=1,r1=0,l2=1,r2=0;
for(int i=1;i<=m;i++){
while(l1<=r1&&i-d>q1[l1])l1++;
while(l1<=r1&&a[q1[r1]]<a[i])r1--;
q1[++r1]=i;
while(l2<=r2&&i-d>q2[l2])l2++;
while(l2<=r2&&b[q2[r2]]<b[i])r2--;
q2[++r2]=i;
if(i>=d)if(n-a[q1[l1]]-b[q2[l2]]<=0)mn=0;
else mn=min(mn,n-a[q1[l1]]-b[q2[l2]]);
}
ans=max(ans,1LL*mn*k);
}
printf("%lld",ans);
return 0;
}