Codeforces Global Round 26 补题记录(A~C2)
下大分场/ll
A
若 \(a\) 的值全部相同则无解,否则:
- 存在一个数出现了至少两次。那么让其中的恰好一次染上红色,其他的数全部染上蓝色。
- 不存在一个数出现了至少两次。那么随机选取一个数染上红色,其他的数全部染上蓝色。
时间复杂度为 \(O(n)\)。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000100;
int a[N];
signed main() {
int T;
cin>>T;
while(T--){
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
if(a[1]==a[n])cout<<"NO\n";
else{
cout<<"YES\n";
map<int,int>mp;
for(int i=1;i<=n;i++)mp[a[i]]++;
for(int i=1;i<=n;i++)if(mp[a[i]]>=2){
for(int j=1;j<=n;j++)if(j==i)cout<<"R";else cout<<"B";
cout<<'\n';
goto ee;
}
cout<<"R";
for(int i=2;i<=n;i++)cout<<"B";
cout<<'\n';
ee:;
}
}
}
B
考虑从低位到高位模拟过程。如果在枚举到某一位的值的时候,值为 \(9\),那么因为 \(9\) 和 \(19\) 均不可以被分成两个 \(5\sim 9\) 的数的和,所以必然无解。否则必然可以分成两个 \(5\sim 9\) 的数的和。设这个位置的值为 \(x\),那么会让答案减去 \(10+x\)。如果减到一个位置的时候,答案不够减,那么就必然存在无解。如果枚举到第一位的时候剩下的值仍然不为 \(0\),那么无解(没有地方可以退位),否则有解。
时间复杂度为 \(O(|n|)\),其中 \(|n|\) 重定义为 \(n\) 在 \(10\) 进制下的位数。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000100;
char s[N];
int a[N];
signed main() {
int T;
cin>>T;
while(T--){
scanf("%s",s+1);
bool ok=true;
int n=strlen(s+1);
for(int i=1;i<=n;i++)a[i]=s[i]^48;
a[0]=a[n+1]=a[n+2]=a[n+3]=a[n+4]=a[n+5]=0;
for(int i=n;i>1;i--){
if(a[i]==9){ok=false;break;}
a[i-1]--;
for(int j=i-1;j;j--){
if(a[j]>=0)break;
a[j-1]--,a[j]+=10;
}
if(a[0]<0){ok=false;break;}
}
cout<<((ok&&!a[1])?"YES":"NO")<<'\n';
}
}
C1
发现要么一直不用 \(2\) 操作,要么在答案取到最小值的时候使用一遍 \(2\) 操作让其变为相对大一些的值。显然使用多遍 \(2\) 操作是不优的。然后还能发现若存在一个位置 \(p\) 使得前缀和 \(<0\),那么这个地方不用 \(2\) 操作肯定不如用 \(2\) 操作优。因此若不存在一个前缀和的值 \(<0\) 那么就不应该使用 \(2\) 操作。否则一定在任意一个值最小的位置使用 \(2\) 操作即可。
时间复杂度为 \(O(n)\)。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000100;
int a[N],pre[N];
signed main() {
int T;
cin>>T;
while(T--){
int n,id=-1;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i],pre[i]=pre[i-1]+a[i];
int mi=0;
for(int i=n;i;i--)if(pre[i]<mi){
mi=pre[i];
id=i;
// break;
}
if(id==-1)cout<<pre[n]<<'\n';
else{
int s=0;
for(int i=1;i<=n;i++){
if(i<=id)s-=a[i];
else s+=a[i];
}
cout<<s<<'\n';
}
}
}
C2
不会猜结论。每一步结束之后,若当前的前缀和取到了全部前缀和的最小值,则这一步对答案造成了 \(2^{n-i+r}\),其中 \(r\) 是在这一步操作中前缀和 \(\ge0\) 的位置的数量。若全部操作中没有任何一个前缀和 \(<0\) 则答案为 \(2^n\)。考虑这个为什么是对的。
- 不存在任何一个前缀和使得这个前缀和 \(\le 0\)。此时对于任意一个位置 \(1\) 操作和 \(2\) 操作没有任何本质区别。任意一步都可以在 \(1\) 操作和 \(2\) 操作中任选一个操作执行。
- 存在一个前缀和使得这个前缀和 \(\le0\)。因为执行完这一步之后后面不论怎么走答案都不会偏离最大值(参见 C1 的结论),所以说后面的任意一步都可以在 \(1\) 操作和 \(2\) 操作中任选,即 \(2^{n-i}\) 种不同选择;若前面有前缀和 \(\ge 0\) 的那么 \(1\) 操作和 \(2\) 操作没有本质的区别,若有 \(r\) 个满足这样条件的则有 \(2^r\) 种不同选择。根据乘法原理可知对答案的贡献为 \(2^{n-i+r}\)。其中 \(r\) 可以在枚举的时候扫一遍得出答案。
时间复杂度为 \(O(n)\)。
Brute Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000100;
int a[N],pre[N];
signed main() {
int T;
cin>>T;
while(T--){
int n,id=-1;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i],pre[i]=pre[i-1]+a[i];
int mi=0;
for(int i=n;i;i--)if(pre[i]<mi){
mi=pre[i];
id=i;
// break;
}
if(id==-1){
cout<<pre[n]<<'\n';
int s=pre[n];
int cnt=0;
for(int i=0;i<(1ll<<n);i++){
int now=0;
for(int j=1;j<=n;j++)if(i>>(j-1)&1)now+=a[j];
else now=abs(now+a[j]);
if (now==s)cnt++;
}
cout<<cnt<<'\n';
}
else{
int s=0;
for(int i=1;i<=n;i++){
if(i<=id)s-=a[i];
else s+=a[i];
}
cout<<s<<'\n';
int cnt=0;
for(int i=0;i<(1ll<<n);i++){
int now=0;
for(int j=1;j<=n;j++)if(i>>(j-1)&1)now+=a[j];
else now=abs(now+a[j]);
if (now==s)cnt++;
}
cout<<cnt<<'\n';
}
}
}
Correct Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000100,mod=998244353;
int a[N],pre[N],bin[N];
signed main() {
int T;
cin>>T;
bin[0]=1;
for(int i=1;i<N;i++)bin[i]=bin[i-1]*2%mod;
while(T--){
int n,id=-1;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i],pre[i]=pre[i-1]+a[i];
int mi=0,r=0;
for(int i=1;i<=n;i++)mi=min(mi,pre[i]);
if(mi==0)cout<<bin[n]<<'\n';
else{
int ans=0;
for(int i=1;i<=n;i++){
if(pre[i]>=0)r++;
if(pre[i]==mi)ans=(ans+bin[n-i+r])%mod;
}
cout<<ans<<'\n';
}
}
}
本文来自博客园,作者:yhbqwq,转载请注明原文链接:https://www.cnblogs.com/yhbqwq/p/18240364,谢谢QwQ