Codeforces Round 1073 Div1 + Div2 部分题目题解

比赛传送门:Codeforces Round 1073

打的 Div2。AB 赛时切了。

C 题解

\(s\) 升序排序后为 \(t\),那么如果 \(s=t\) 显然 Bob 胜利,否则我们断言 Alice 一定胜利。

如果 Alice 可以一步将 \(s\) 变成 \(t\),那么他就赢了,而我们发现如果 Alice 操作 \(s\)\(t\) 不相等那部分,显然可以将其排序,做完了。

#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read(){
	char c=getchar();
	int f=1,ans=0;
	while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
	while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
	return ans*f;
}
inline void solve(){
	int n=read();
	string s,t;cin>>s;t=s;
	sort(t.begin(),t.end());
	if (s==t){
		puts("Bob");
		return ;
	}
	puts("Alice");
	vector<int>anss;
	for (int i=0;i<n;i++) if (s[i]!=t[i]) anss.push_back(i);
	printf("%lld\n",(int)anss.size());
	for (auto i:anss) printf("%lld ",i+1);puts("");
}
main(){
	int T=read();
	while(T--) solve(); 
    return 0;
}

D1 题解

枚举使得 \(t\)\(s\) 优的位置 \(i\) 满足 \(s_i\) 为右括号,那么 \(t\)\(i-1\)\(s\)\(i-1\) 位相同,找到第一个 \(j>i\) 满足 \(s_j\) 为左括号。

因此 \(t\) 中我们一定不能选 \(i+1\)\(j\) 这些位置,为了让 \(t\) 是正则的,后面还不能选 \(j-i\) 个左括号,判一下是否有这么多即可,答案即为 \(\max\{n-2(j-i)\}\)

证明这样的 \(t\) 是正则的,可以把左括号看成 \(1\),右括号看成 \(-1\),然后分析即可。

所以显然答案只有 \(-1\)\(n-2\)

#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
const int N=2e5+10; 
inline int read(){
	char c=getchar();
	int f=1,ans=0;
	while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
	while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
	return ans*f;
}
int n,a[N],b[N];
inline void solve(){
	n=read();
	string s;cin>>s;s=" "+s; 
	for (int i=0;i<=n+1;i++) a[i]=b[i]=0;
	for (int i=n;i>0;i--) b[i]=b[i+1]+(s[i]=='(');
	a[n+1]=n+1;
	for (int i=n;i>0;i--) if (s[i]=='(') a[i]=i;else a[i]=a[i+1];
	int ans=-1;
	for (int i=1;i<=n;i++) if (s[i]==')'&&a[i]<=n&&b[a[i]+1]>=a[i]-i) ans=max(ans,n-2*(a[i]-i));
	printf("%lld\n",ans);
}
main(){
	int T=read();
	while(T--) solve(); 
    return 0;
}

D2 题解

首先你要会 D1,容易发现这里的贡献只有 \(0,|t|-2\) 两种。

主播主播,官解 \(O(n^3)\) 确实很强,但还是太吃脑子了,有没有更加简单强势的 \(O(n^4)\) 推荐一下。

有的兄弟有的,我们发现如果这个子序列要有贡献,当且仅当存在位置 \(i\) 使得 \(s_i=\texttt{)}\)\(s_{i+1}=\texttt{(}\),且存在 \(j>i+1\) 使得 \(s_j=\texttt{(}\)

按照这个设计 dp,设 \(f_{i,j,k,0/1,0/1}\),以 \(i\) 结尾的子序列,且子序列长度为 \(j\),且子序列中的左括号看成 \(1\),右括号看成 \(-1\) 的总和为 \(k\),是否存在连续的 \(\texttt{)(}\) 以及之后是否存在 \(\texttt{(}\) 的方案数。

转移的话枚举上一位分讨,记上一位为 \(l\)

  1. \(s_i=\texttt{(}\)\(s_l=\texttt{(}\)

\[f_{l,j-1,k-1,0,0} \to f_{i,j,k,0,0} \]

\[f_{l,j-1,k-1,1,0} \to f_{i,j,k,1,0} \]

\[f_{l,j-1,k-1,1,1} \to f_{i,j,k,1,1} \]

  1. \(s_i=\texttt{(}\)\(s_l=\texttt{)}\)

\[f_{l,j-1,k-1,0,0} \to f_{i,j,k,0,0} \]

\[f_{l,j-1,k-1,0,0} \to f_{i,j,k,1,0} \]

\[f_{l,j-1,k-1,1,0} \to f_{i,j,k,1,1} \]

  1. \(s_i=\texttt{)}\)

\[f_{l,j-1,k+1,0,0} \to f_{i,j,k,0,0} \]

\[f_{l,j-1,k+1,1,0} \to f_{i,j,k,1,0} \]

\[f_{l,j-1,k+1,1,1} \to f_{i,j,k,1,1} \]

最后答案直接枚举 \(i,j\),贡献即为 \(f_{i,j,0,1,1}(j-2)\)

#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read(){
	char c=getchar();
	int f=1,ans=0;
	while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
	while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
	return ans*f;
}
const int N=110,mod=998244353;
int f[N][N][N][2][2],n;
string s;
inline void add(int &x,int y){x=(x+y)%mod;}
inline void solve(){
	n=read();cin>>s;s=" "+s;
	for (int i=0;i<=n+1;i++) for (int j=0;j<=n+1;j++) for (int k=0;k<=n;k++) f[i][j][k][0][0]=f[i][j][k][0][1]=f[i][j][k][1][0]=f[i][j][k][1][1]=0;
	for (int i=1;i<=n;i++) if (s[i]=='(') f[i][1][1][0][0]=1;
	for (int i=2;i<=n;i++) for (int j=1;j<=n;j++) for (int k=0;k<=n;k++) for (int l=1;l<i;l++)
		if (s[i]=='('&&s[l]=='('&&k-1>=0){
			add(f[i][j][k][0][0],f[l][j-1][k-1][0][0]);
			add(f[i][j][k][1][0],f[l][j-1][k-1][1][0]);
			add(f[i][j][k][1][1],f[l][j-1][k-1][1][0]);	
		}
		else if (s[i]=='('&&s[l]==')'&&k-1>=0){
			add(f[i][j][k][0][0],f[l][j-1][k-1][0][0]);
			add(f[i][j][k][1][0],f[l][j-1][k-1][0][0]);
			add(f[i][j][k][1][1],f[l][j-1][k-1][1][0]);	
		}
		else if (s[i]==')'&&k+1<=n){
			add(f[i][j][k][0][0],f[l][j-1][k+1][0][0]);
			add(f[i][j][k][1][0],f[l][j-1][k+1][1][0]);
			add(f[i][j][k][1][1],f[l][j-1][k+1][1][1]);
		}
	int ans=0;
	for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) add(ans,f[i][j][0][1][1]*(j-2)%mod);
	printf("%lld\n",ans); 
}
main(){
	int T=read();
	while(T--) solve(); 
    return 0;
}

主播主播,你的 \(O(n^4)\) 确实很强,但还是太吃时间了,有没有更加简单强势的 \(O(n^3)\) 推荐一下。

有的兄弟有的,我们发现如果这个子序列要有贡献,当且仅当存在位置 \(i\) 使得 \(s_i=\texttt{)}\)\(s_{i+1}=\texttt{(}\),且存在 \(j>i+1\) 使得 \(s_j=\texttt{(}\)

考虑放宽限制,可以发现只需存在一个子序列 \(\texttt{)((}\) 即可,证明考虑调整法调整成前面的情况。

这样就可以考虑 dp 了,设 \(f_{i,j,k,0/1/2/3}\) 表示前 \(i\) 个位置长度为 \(j\) 的子序列且子序列中的左括号看成 \(1\),右括号看成 \(-1\) 的总和为 \(k\),且子序列匹配了 \(\texttt{)((}\) 的前 \(0/1/2/3\) 位。

转移考虑按照这一位选择不不选择分讨。

  1. 不选择 \(i\) 位置。

\[f_{i-1,j,k,0}\to f_{i,j,k,0} \]

\[f_{i-1,j,k,1}\to f_{i,j,k,1} \]

\[f_{i-1,j,k,2}\to f_{i,j,k,2} \]

\[f_{i-1,j,k,3}\to f_{i,j,k,3} \]

  1. 选择 \(i\)\(s_i=\texttt{(}\)

\[f_{i-1,j-1,k-1,0}\to f_{i,j,k,0} \]

\[f_{i-1,j-1,k-1,1}\to f_{i,j,k,1} \]

\[f_{i-1,j-1,k-1,1}\to f_{i,j,k,2} \]

\[f_{i-1,j-1,k-1,2}\to f_{i,j,k,3} \]

  1. 选择 \(i\)\(s_i=\texttt{)}\)

\[f_{i-1,j-1,k+1,0}\to f_{i,j,k,0} \]

\[f_{i-1,j-1,k+1,0}\to f_{i,j,k,1} \]

\[f_{i-1,j-1,k+1,2}\to f_{i,j,k,2} \]

\[f_{i-1,j-1,k+1,3}\to f_{i,j,k,3} \]

最后答案枚举 \(j\),贡献即为 \(f_{n,j,0,3}(j-2)\)

#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read(){
	char c=getchar();
	int f=1,ans=0;
	while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
	while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
	return ans*f;
}
const int N=110,mod=998244353;
int f[N][N][N][4],n;
string s;
inline void add(int &x,int y){x=(x+y)%mod;}
inline void solve(){
	n=read();cin>>s;s=" "+s;
	for (int i=0;i<=n+1;i++)
		for (int j=0;j<=n+1;j++)
			for (int k=0;k<=n+1;k++)
				for (int l=0;l<4;l++) f[i][j][k][l]=0;
	f[0][0][0][0]=1;
	for (int i=1;i<=n;i++) for (int j=0;j<=n;j++) for (int k=0;k<=n;k++){
		add(f[i][j][k][0],f[i-1][j][k][0]);
		add(f[i][j][k][1],f[i-1][j][k][1]);
		add(f[i][j][k][2],f[i-1][j][k][2]);
		add(f[i][j][k][3],f[i-1][j][k][3]);
		if (s[i]=='('&&j-1>=0&&k-1>=0){
			add(f[i][j][k][0],f[i-1][j-1][k-1][0]);
			add(f[i][j][k][1],f[i-1][j-1][k-1][1]);
			add(f[i][j][k][2],f[i-1][j-1][k-1][1]);
			add(f[i][j][k][3],f[i-1][j-1][k-1][2]);
		}
		if (s[i]==')'&&j-1>=0&&k+1<=n){
			add(f[i][j][k][0],f[i-1][j-1][k+1][0]);
			add(f[i][j][k][1],f[i-1][j-1][k+1][0]);
			add(f[i][j][k][2],f[i-1][j-1][k+1][2]);
			add(f[i][j][k][3],f[i-1][j-1][k+1][3]);
		}
	}
	int ans=0;
	for (int i=2;i<=n;i++) add(ans,f[n][i][0][3]*(i-2)%mod);
	printf("%lld\n",ans);
}
main(){
	int T=read();
	while(T--) solve(); 
    return 0;
}
posted @ 2026-01-18 19:32  OTn53_qwq  阅读(227)  评论(0)    收藏  举报