牛客 小白111 20250307

牛客 小白111 20250307

https://ac.nowcoder.com/acm/contest/102742

A:

题目大意:给定三个 \(v\) 三个 \(a\) ,模拟田忌赛马的策略判断是否获胜

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
 
using namespace std;

int main()
{
	int v[3],a[3];
	cin>>v[0]>>v[1]>>v[2];
	cin>>a[0]>>a[1]>>a[2];
	sort(v,v+3);
	sort(a,a+3);
	if (a[1]>v[0]&&a[2]>v[1]) cout<<"Yes";
	else cout<<"No";
	return 0;
}

排序,选 \(a\) 中两个最大的和 \(v\) 中两个最小的比

B:

题目大意:给定字符串 如果有连续的偶数个 \(n\) 可以使这个连续子串变为 \(y\) ,判断最后至多有多少个 \(y\)

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
 
using namespace std;

int main()
{
	int n;
	cin>>n;
	vector<char> a(n+10);
	for (int i=1;i<=n;i++) cin>>a[i];
	int sum=0;
	for (int i=1;i<=n;i++){
        if (a[i]=='y') sum++;
		if (a[i]=='n'&&a[i+1]=='n'){
            sum++;
            i++;
        }
	}
	cout<<sum;
	return 0;
}

贪心策略,当有两个连续的 \(n\) ,那么就即可转变为 \(y\)

C:

题目大意:有 \(n\) 条字符串,可以选取其中一条删去,连续且相同的字符串的最多条数

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
 
using namespace std;

int dp[100010][2];

int main()
{
	int n;
	cin>>n;
	vector<string> s(n+5);
	int ans=1;
	for (int i=1;i<=n;i++)
		cin>>s[i];
	for (int i=1;i<=n;i++) dp[i][1]=dp[i][0]=1;
	for (int i=2;i<=n;i++){
		for (int j=0;j<=1;j++){
			if (s[i]==s[i-1])
				dp[i][j]=dp[i-1][j]+1;
			if (i>=3&&s[i]==s[i-2]&&j==1)
				dp[i][j]=max(dp[i][1],dp[i-2][0]+1);
			ans=max(ans,dp[i][j]);
		}
	}
	cout<<ans;
	return 0;
}

我比较笨,上来就写个DP,\(dp_{i,j}\) 表示前 \(i\) 个字符串删去 \(j\) 条字符的连续且相同的字符串的最多条数

事实上可以利用滑动窗口的方法解决,虽然时间复杂度不如DP

map<string,int> mp;
int l=1;
int ans=0;
for (int r=1;r<=n;r++){
	mp[s[r]]++;
	while(mp.size()>2||min(mp.begin()->second,mp.rbegin()->second)>1&&mp.size()>1){
		mp[s[l]]--;
		if (mp[s[l]]==0) mp.erase(s[l]);
		l++;
	}
	if (mp.size()==1) ans=max(ans,r-l+1);
	else ans=max(ans,r-l);
}
cout<<ans;

mp 记录滑动窗口中不同的字符串以及它的数量

向右滑动后,新加入窗口的字符串可以对状态有三种改变

  • 窗口内的字符串种类数多于 \(2\)
  • 窗口内仍然最多只有两个字符串,其中数量最少的字符串的个数多于 \(1\)
  • 窗口内仍然最多只有两个字符串,且数量最少的字符串的个数少于等于 \(1\)

其中前两个状态不满足题目要求,我们最多只能删去窗口内的一条字符串

while(mp.size()>2||min(mp.begin()->second,mp.rbegin()->second)>1&&mp.size()>1){
	mp[s[l]]--;
	if (mp[s[l]]==0) mp.erase(s[l]);
	l++;
}

并且如果移动左指针时,对应的字符串数量变为 \(0\)需要立即将这个字符串从 mp 内擦去

D:

题目大意:

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
 
using namespace std;

int n,m,T;
int a[1010][1010];
int g[1010][1010];
int dp[1010][1010];
int ans;

int main()
{
	memset(g,0x3f,sizeof g);//没有障碍的位置可以看作在极长的时间后才生成障碍
	cin>>n>>m;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			cin>>a[i][j];
	cin>>T;
	for (int i=1;i<=T;i++){
		int x,y,v;
		cin>>x>>y>>v;
		g[x][y]=v;
	}
	
	dp[1][1]=a[1][1];
	for (int i=1;i<=n;i++){
		for (int j=1;j<=m;j++){
			if (i==1&&j==1) continue;
			if (i+j-1>g[i][j]) continue;
			if (i-1>0&&dp[i-1][j])
				dp[i][j]=max(dp[i-1][j]+a[i][j],dp[i][j]);
			if (j-1>0&&dp[i][j-1])
				dp[i][j]=max(dp[i][j-1]+a[i][j],dp[i][j]);
		}
	}
	
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			ans=max(ans,dp[i][j]);
	cout<<ans;
	return 0;
}

一开始用BFS搜,因为每个点不止入队一次,所以空间爆炸

改用DP,类似过河卒的做法,加入的随时间生成的障碍

又因为每个时刻只能走一个,且只能向下或向右,所以可以通过坐标计算时间 \(t=i+j-1\)

\((1,1)\) 出发,开始递推DP,状态转移方程为

\[dp_{i,j}=\begin{cases} {\rm{max}}(dp_{i-1,j}+a_{i,j},dp_{i,j})&\text{$dp_{i-1,j}\ne0$} \\ {\rm{max}}(dp_{i,j-1}+a_{i,j},dp_{i,j})&\text{$dp_{i,j-1}\ne0$} \end{cases} \]

因为在收集金币时,必须从已经走过的路径上转移,所以需要保证 \(dp_{i-1,j},dp_{i,j-1}\) 都不为 \(0\)

考虑随时间生成的障碍和边界情况,添加两条规则

if (i==1&&j==1) continue;
if (i+j-1>g[i][j]) continue;

E:

题目大意:

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
 
using namespace std;

LL n,m,k;

int main()
{
	cin>>n>>m>>k;
	vector<LL> a(n+1,0),pre=a;
	for (int i=1;i<=n;i++) cin>>a[i];
	for (int i=1;i<=n;i++) pre[i]=pre[i-1]+a[i];
	LL ans=0;
	for (int i=1;i<=n;i++){
		int L=upper_bound(a.begin(),a.end(),k+a[i])-a.begin();
		int R=lower_bound(a.begin(),a.end(),m+a[i]+k+1)-1-a.begin();
		int l=i+1;
		int r=lower_bound(a.begin(),a.end(),m+a[i]-k+1)-1-a.begin();
		ans+=1ll*(r-l+1)*(m+a[i]-k+1)-(pre[r]-pre[i]);
		if (R>=L) ans+=1ll*(R-L+1)*(m+a[i]+k+1)-(pre[R]-pre[L-1]);
	}
	cout<<ans;
	return 0;
}

推柿子题

\(d=a_j-a_i\),高为 \(h\) ,那么 \(k=\lvert d-h\rvert\),考虑两种情况

  • \(k=d-h\implies h=d-k\) 可以构成矩形的数量为

    \[\sum_{i=1}^{n}\sum_{j=i+1}^{n}(m-h+1)=\sum_{i=1}^{n}\sum_{j=i+1}^{n}(m-(a_j-a_i-k)+1) \]

    此外还需要保证

    \[a_j-a_i>k\ \ ,\ \ m-(a_j-a_i-k)+1>0\\ \implies a_j>k+a_i\ \ ,\ \ a_j<m+a_i+k+1 \]

  • \(k=h-d\implies h=d+k\) 可以构成矩形的数量为

    \[\sum_{i=1}^{n}\sum_{j=i+1}^{n}(m-h+1)=\sum_{i=1}^{n}\sum_{j=i+1}^{n}(m-(a_j-a_i+k)+1) \]

    此外还需要保证

    \[a_j-a_i>0\ \ ,\ \ m-(a_j-a_i+k)+1>0\\ \implies a_j>a_i\ \ ,\ \ a_j<m+a_i-k+1 \]

设满足第一种情况的 \(j\in[L,R]\) ,第二种情况的 \(j\in[l,r]\),可以通过二分查找实现

最后整理公式得到

\[ans=\sum_{i=1}^{n} \left[ (R-L+1)*(m+a_i+k+1)-\sum_L^R a_j\right]+ \sum_{i=1}^{n} \left[ (r-l+1)*(m+a_i-k+1)-\sum_l^r a_j\right] \]

其中的 \(\sum a_j\) 可以通过前缀和预处理,算法总时间复杂度为 \(O(n\log n)\)

int L=upper_bound(a.begin(),a.end(),k+a[i])-a.begin();
int R=lower_bound(a.begin(),a.end(),m+a[i]+k+1)-a.begin()-1;
//不减一查找的是 a_j >= m+a_i+k+1 的第一个元素,减一后才在范围内
int l=i+1;
int r=lower_bound(a.begin(),a.end(),m+a[i]-k+1)-a.begin()-1;
//不减一查找的是 a_j >= m+a_i-k+1 的第一个元素,减一后才在范围内

并且只有当 \(r>l,R>L\) 时答案才能正确累加

ans+=1ll*(r-l+1)*(m+a[i]-k+1)-(pre[r]-pre[i]);//r一定大于l,因为m-k+1>0
if (R>=L) ans+=1ll*(R-L+1)*(m+a[i]+k+1)-(pre[R]-pre[L-1]);
posted @ 2025-03-16 21:19  才瓯  阅读(36)  评论(0)    收藏  举报