下大分場/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';
}
}
}