1. 程式人生 > 實用技巧 >暑 假 隊 測 Round #5

暑 假 隊 測 Round #5

\(30+80+30=140pts\)
最菜的一次,題目不難,但打的全是暴力。
T2資料過水,假貪心騙了80分。

T1:尋找羔羊
T2:序列
T3:硬幣

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; 
}