牛客 周赛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