牛客 周赛87 20250401

牛客 周赛87 20250401

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

A:

题目大意:给定 \(a,b,c\) 判断是否 \(b<a,b<c\) 成立

#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 ULL unsigned long long 

using namespace std;

void solve(){
	int a,b,c;
	cin>>a>>b>>c;
	if (b<a&&b<c) cout<<"YES";
	else cout<<"NO";
}

int main()
{
	cintie;
	solve();
	
	
	return 0;
}

签到

B:

题目大意:给定正整数 \(n\) ,将 \(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 ULL unsigned long long 

using namespace std;

void solve(){
	string s;
	cin>>s;
	int a1=s.front()-'0',b1=s.back()-'0';
	int a2=stoi(s.substr(1,s.size())),b2=stoi(s.substr(0,s.size()-1));
    cout<<max(a1+a2,b1+b2)<<endl;
}

int main()
{
	cintie;
	Trd;
	
	
	return 0;
}

贪心的想法,一定是分割为一个极大位的整数和一个极小位的整数加起来最优,所以判断分割首位位置即可
忘了字符串转整数的函数,用 while 慢慢取位做的

补一下函数

long long stoll(const std::string& str, size_t* pos = nullptr, int base = 10);
int stoi(const std::string& str, size_t* pos = 0, int base = 10);

此外还有 substr 函数用来取连续子串

substr(size_type _Off = 0,size_type _Count = npos)

返回值: string,包含 s 中从 pos 开始的 len 个字符的拷贝( pos 的默认值是 \(0\)len 的默认值是 s.size() - pos,即不加参数会默认拷贝整个 s

C:

题目大意:

#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 ULL unsigned long long 
 
using namespace std;
 
void solve(){
    int n;
    cin>>n;
    vector<LL> a(n+10);
    for (int i=1;i<=n;i++) cin>>a[i];
    string s;
    cin>>s;
    s=" "+s;
    int ans=0;
    for (int i=1;i<=n;i++){
        if (s[i]=='>'&&a[i]<=0){
            ans++;
            a[i]=1;
        } 
        if (s[i]=='<'&&a[i]>=0){
            ans++;
            a[i]=-1;
        }
        if (s[i]=='Z'&&a[i]*a[i-1]<=0){
            ans++;
            a[i]=a[i-1];
        }
    }
    cout<<ans<<endl;
}
 
int main()
{
    cintie;
    Trd;
     
     
    return 0;
}

差点翻车,第一眼没看完题目,然后拿DP写了半天,最后也算是凑对了

赛后重新看了一下,其实很简单,还是贪心

如果当前的 \(s_i\)\(a_i\) 矛盾,那么一定要改,特别的可以直接改成满足 \(s_i\) 的特值 \(\pm1\)

如果当前 \(s_i\)\(Z\) ,并且 \(a_{i-1}*a_i\le0\) 则需要更改 \(a_i\)还是可以改为与 \(a_{i-1}\) 相同的值

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 ULL unsigned long long 

using namespace std;

LL a[200010];
LL dp[200010];

void solve(){
	int n;
	cin>>n;
	for (int i=1;i<=n;i++) cin>>a[i];
	memset(dp,-0x3f,sizeof dp);
	dp[0]=0;
	for (int i=1;i<=n;i++){
		dp[i]=max(dp[i],dp[i-1]+a[i]);
		if (i>=2)
			dp[i]=max(dp[i],dp[i-2]);
		if (i>=3)
			dp[i]=max(dp[i],dp[i-3]);
	}
	cout<<dp[n]<<endl;
}

int main()
{
	cintie;
	Trd;
	
	
	return 0;
}

一眼DP,但是不开 long long 见祖宗,定义 \(dp_i\) 表示前 \(i\) 个位置上的最大和,考虑状态转移

  • 如果当前位置不删,那么 \(dp_i=dp_{i-1}+a_i\)

  • 如果当前位置是第一种删法,那么 \(dp_i=dp_{i-2}\) ,即不取 \(a_{i-1},a_{i-2}\)

  • 如果当前位置是第二种删法,那么 \(dp_i=dp_{i-3}\) ,即不取 \(a_{i-1},a_{i-2},a_{i-3}\)

初始化为极小值且 \(dp_0=0\)

同样的,主动转移也可以写出来

for (int i=0;i<=n;i++){
	if (i+1<=n)
		dp[i+1]=max(dp[i+1],dp[i]+a[i+1]);
	if (i+2<=n)
		dp[i+2]=max(dp[i+2],dp[i]);
	if (i+3<=n)
		dp[i+3]=max(dp[i+3],dp[i]);
}

E:

题目大意:构造一个长度为 \(n\) 的数组 \(a\) ,满足

  • \(a_1\ |\ a_2\ |\ a_3\ |\ \cdots\ |\ a_n=x\),其中 \(|\) 表示按位或运算

  • \(a_1\oplus a_2\oplus a_3\oplus \cdots\oplus a_n=y\),其中 \(\oplus\) 表示按位其中 \(|\) 表示按位异或运算

  • \(1\le a_i<2^{31}\)

#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 ULL unsigned long long 

using namespace std;

void solve(){
	int n,x,y;
	cin>>n>>x>>y;
	if ((x&y)!=y){
		cout<<"NO"<<endl;
		return;
	}
	vector<int> ans(n+10);
	int skip=1;
	for (int j=31;j>=0;j--){
		int a=(x>>j)&1,b=(y>>j)&1;
		if (a==0) continue;
		int now=(a^b)^(n&1);
		if (now){
			for (int i=1;i<=n;i++)
				ans[i]|=(1<<j);
		}else{
			for (int i=1;i<=n;i++){
				if (i==skip) continue;
				ans[i]|=(1<<j);
			}
			skip=min(skip+1,n);
		}
	}
	
	for (int i=1;i<=n;i++){
		if (ans[i]<=0){
			cout<<"NO"<<endl;
			return;
		}
	}
	int a=0,b=0;
	for (int i=1;i<=n;i++){
		a|=ans[i];
		b^=ans[i];
	}
	if (a!=x||b!=y){
		cout<<"NO"<<endl;
		return;
	}
	cout<<"YES"<<endl;
	for (int i=1;i<=n;i++) cout<<ans[i]<<' ';
	cout<<endl;
}

int main()
{
	cintie;
	Trd;
	
	
	return 0;
}

比较神奇的构造题,没有凑出来,这里记所需的数组 \(a\)\(ans\)

首先考虑第一和第二个性质,可以发现 \(x\) 的某一位为 \(0\) ,那么 \(y\) 的这一位一定也为 \(0\),同样的,如果 \(x\) 的某一位为 \(1\) ,那么 \(y\) 的这一位取 \(1,0\) 都可以,所以可以得出第一个特判的条件:\(x\&y=y\) 时,才有合适的数组 \(a\)

然后考虑贪心构造 \(ans_i\),先枚举每一位

int a=(x>>j)&1,b=(y>>j)&1;//a表示x在第j位上是0还是1,同理有b

如果当前的 \(a\)\(0\) ,则所有的 \(ans_i\) 在当前 \(j\) 位上一定都为 \(0\),又因为初始化时 \(ans_i=0\) ,所以直接跳过

现在考虑 \(a=1\) 时的情况,这里存在一个异或运算的性质,\(n\)\(1\) 异或时,如果 \(n\) 为奇,那么结果为 \(1\) ,否则为 \(0\)

所以定义 now 表示当前的情况

int now=(a^b)^(n&1);

如果 \(b=1\) 说明当前的 \(j\) 位上有奇数个 \(1\) ,如果 \(b=0\) 说明当前的 \(j\) 位上有偶数个 \(1\)

然后加入 \(n\) 的奇偶性并判断是否需要 \(0\)\(ans_i\)\(j\) 位上,这里贪心的考虑加入更多 \(1\)

这样贪心的原因在于如果加入更多的 \(0\) ,则 \(a_i\) 可能为 \(0\) ,不满足条件,所以可能会劣,所以要让 \(ans_i\) 中的 \(0\) 尽可能的少

既然 \(0\) 尽可能的少,那就让满足 \(x\)\(j\) 位为 \(1\) 时,\(ans_i\)\(j\) 位就只有一个 \(0\),并且每次第 \(j\) 位为 \(0\)\(ans_i\) 都不同

int skip=1;//第skip个ans在j位上需要为0
for (int i=1;i<=n;i++){
	if (i==skip) continue;//当前的ans在j位上为0
	ans[i]|=(1<<j);//累或计算ans
}
skip=min(skip+1,n);//skip不能超过n

经过上面一顿构造出的数组 \(ans\) 一定是最接近答案的,即当前 \(x,y\) 约束下的最优解

除此之外还要继续判断当前构造出的 \(ans_i\) 是否满足第三个条件

for (int i=1;i<=n;i++){
	if (ans[i]<=0){
		cout<<"NO"<<endl;
		return;
	}
}

最后还要进一步验证,是否完全满足三个条件(不然只有 \(97\%\),虽然还是不知道为什么

int a=0,b=0;
for (int i=1;i<=n;i++){
	a|=ans[i];
	b^=ans[i];
}
if (a!=x||b!=y){
	cout<<"NO"<<endl;
	return;
}

F:

题目大意:

#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 ULL unsigned long long 

using namespace std;

int calc(int l,int r,string s){
	for (int i=1;i<s.size();i++){
		if (s[i]!='?') continue;
		if (i<l||i>r) s[i]='o';
		else s[i]='v';
	}
	int o=0,ov=0,ovo=0;
	for (int i=1;i<s.size();i++){
		if (s[i]=='o'){
			o++;
			ovo+=ov;
		}else
			ov+=o;
	}
	return ovo;
}

void solve(){
	string s;
	cin>>s;
	s=" "+s;
	int ans=0;
	for (int l=1;l<s.size();l++)
		for (int r=l-1;r<s.size();r++)
			ans=max(ans,calc(l,r,s));
	cout<<ans<<endl;
}

int main()
{
	cintie;
	Trd;
	
	
	return 0;
}

猜结论题,如果把 ? 改为 v ,那么一定是一段连续的 v ,换句话说在 ? 中只能把区间 \([l,r]\) 内的 ? 改为 v ,区间外的 ? 都要改为 o

可以从这样一个直观的问题来考虑,如果需要在一段连续的 o 字符串内加入一个 v ,显然的,一定是把这个 v 放在最靠近中间的位置上最优

  • 假设 v 左边的 o 数量为 \(a\) ,右边的数量为 \(b\) ,则有 \(a+b=n\)ovo 子串个数为 \(a*b\)

这样当且仅当 \(a\) 最接近 \(b\)\(a*b\) 最大

同样的,在题目给定的字符串 \(s\) 中,假设把一个 ? 改为 v ,一定存在某一个位置使得修改后的字符串中 ovo 子串个数最多

那么考虑把 \(k\)? 改为 v ,这 \(k\) 个最优的位置一定是连续的

所以枚举这一段连续改为 v 的区间 \([l,r]\),然后计算修改后字符串中 ovo 子串个数

可以利用类似DP的方式线性计算子串个数

int o=0,ov=0,ovo=0;//记录o的个数,ov的个数,ovo的个数
for (int i=1;i<s.size();i++){
	if (s[i]=='o'){
		o++;
		ovo+=ov;
	}else
		ov+=o;
}

如果当前枚举出的是 o ,那么 o 的个数加一,ovo 可以从 ov 加上 o 转移过来,所以 ovo+=ov

同理,如果枚举出的是 v ,那么 ov 可以从 o 加上 v 转移过来,所以 ov+=o

posted @ 2025-04-06 22:09  才瓯  阅读(20)  评论(0)    收藏  举报