暑 假 队 测 Round #5
\(30+80+30=140pts\)
最菜的一次,题目较易,但打的全是暴力。
T2数据过水,假贪心骗了80分。
T1:字符串模拟
假使我们找到一个"agnus",
其左端点是\(l\),其右端点是\(r\).(\(1\le l \le r \le n\) )
那么很显然包含它的子串\(x\) ~ \(y\)只需要满足\(x \le l ~\)&&\(~ r \le y\)。
总数量就是\(l \times (n-r+1)\).
避免重复统计,我们需要记录上一个"agnus"的结束位置\(pre\)
统计答案时应是\(ans\) \(+\) \(=\) \((l-pre+1) \times (n-r+1)\)
\(Code\):
#include<bits/stdc++.h>
using namespace std;
string st,s="agnus";
int n,ans,pr;
int main(){
cin>>st;
n=st.size();
for(int i=0;i<n;i++)
if(st.substr(i,5)==s)ans+=(i+1-pr)*(n-i-4),pr=i+1;
printf("%d\n",ans);
return 0;
}
T2:单调队列
一眼会想到贪心求解,
思路很简单,使得当前温度尽可能的低.
温度不够就降下去重新升温,升温尽可能少
但是会被下面这组数据卡掉:
9
3 3
1 3
1 1
2 2
3 3
4 5
4 5
3 5
3 3
我们要做的其实是维护一段连续的子段。
使得其中的温度单调不降。
单调队列可以轻松解决。
\(Code:\)
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,a[N],b[N],q[N],ans;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d %d",&a[i],&b[i]);
int l=1,r=0;
for(int i=1;i<=n;i++){
while(l<=r&&a[i]>a[q[r]])r--;
q[++r]=i;
while(l<=r&&b[i]<a[q[l]])l++;
ans=max(ans,i-q[l-1]);
}
printf("%d\n",ans);
return 0;
}
T3:背包
01背包的转移方程是 \(f[i][j]=f[i-1][j]+f[i-1][j-a[i]]\)
我们可以先做背包求出所有的\(f[i][j]\),
移项:\(f[i-1][j-a[i]]=f[i][j]-f[i-1][j]\);
所以当\(f[i][j]-f[i-1][j]==0\)时此硬币对\(j\)元钱的组成没有贡献。
\(Code:\)
#include<bits/stdc++.h>
using namespace std;
const int N=3e5,M=105;
int ans,a[M],f[N+5];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
f[0]=1;
for(int i=1;i<=n;i++)
for(int j=N;j>=0;j--)
if(f[j]&&j+a[i]<=N)f[j+a[i]]+=f[j];
for(int i=1;i<=n;i++){
ans=0;
for(int j=0;j<=N;j++)
if(f[j]&&j+a[i]<=N)f[j+a[i]]-=f[j];
for(int j=1;j<=N;j++)if(f[j])ans++;
for(int j=N;j>=0;j--)
if(f[j]&&j+a[i]<=N)f[j+a[i]]+=f[j];
printf("%d\n",ans);
}
return 0;
}