CF1680C solution
Problem
给定一个字符串 \(s\),你可以从该字符串开头删除若干个字符(可能为 \(0\)),再从结尾删除若干个字符(可能为 \(0\)),两次删除的代价总和为两次删除的字符串中 \(1\) 的总个数和完成两次删除后的字符串剩余的 \(0\) 的个数中的较大值。
link->https://codeforces.com/contest/1680/problem/C
请使删除代价最小化。
Solution
很有意思的一题,感觉思维难度已经完全超过了普通 2C 甚至达到 2D 的难度。
最简单的思路,我们枚举开头删除几个字符,再枚举结尾删除几个字符,然后计算代价,最后取代价最小值。这样进行计算,枚举时间复杂度 \(\Theta(n^2)\),计算代价复杂度 \(\Theta(n)\),总时间复杂度 \(O(n^3)\),哪怕是 \(n\le1000\) 的极小数据也难以通过。
考虑前缀和优化。
我们约定 \(pre_{0,i}\) 和 \(pre_{1,i}\) 分别为 \(s_{1\sim i}\)(字符串下标从 \(1\) 开始)中 \(0\) 和 \(1\) 的个数。\(nxt_{0,i}\) 和 \(nxt_{1,i}\) 分别为 \(s_{n-i+1\sim n}\) 中 \(0\) 和 \(1\) 的个数。
则删除字符串前 \(a\) 个字符,然后再删除结尾 \(b\) 个字符的代价为 \(\max\{pre_{1,a}+nxt_{1,b},pre_{0,n}-pre_{0,a}-nxt_{0,b}\}\)。这样,计算代价复杂度降至 \(\Theta(1)\),预处理前缀和复杂度为 \(\Theta(n)\),总时间复杂度为 \(\Theta(n^2)\),还需要优化。
为什么复杂度会是 \(n^2\) 呢?是的,主要是因为计算代价中 \(\max\) 的原因。对于代价取两次删除的字符串中 \(1\) 的总个数即 \(pre_{1,a}+nxt_{1,b}> pre_{0,n}-pre_{0,a}-nxt_{0,b}\) 的情况,我们可以将其稍微移项:
\(\quad \quad \quad \quad \quad \quad \quad \quad \;\;pre_{1,a}+nxt_{1,b}> pre_{0,n}-pre_{0,a}-nxt_{0,b}\)
\(\quad \; pre_{1,a}+nxt_{1,b}+pre_{0,a}+nxt_{0,b}\ge pre_{0,n}\)
\((pre_{1,a}+pre_{0,a})+(nxt_{1,b}+nxt_{0,b})> pre_{0,n}\)
\(\quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad a+b> pre_{0,n}\)
也就是,当 \(a+b> pre_{0,n}\) 时,代价取 \(pre_{1,a}+nxt_{1,b}\) 反之取 \(pre_{0,n}-pre_{0,a}-nxt_{0,b}\)。
容易发现,\(pre\) 和 \(nxt\) 序列都是非严格单调递增的。
则,我们想要在代价取 \(pre_{1,a}+nxt_{1,b}\) 的情况下使之最小化,那么已知 \(a\),无疑让 \(b\) 越小越好,而为了满足 \(a+b> pre_{0,n}\),我们让 \(b=pre_{0,n}-a+1\) 时最佳。
我们想要在代价取 \(pre_{0,n}-pre_{0,a}-nxt_{0,b}\) 的情况下使之最小化,那么已知 \(a\),无疑让 \(b\) 越大越好,而为了满足 \(a+b\le pre_{0,n}\),我们让 \(b=pre_{0,n}-a\) 时最佳。
这样,我们只需要枚举 \(a\),然后 \(\Theta(1)\) 最优化 \(b\),计算代价取最小值即可。时间复杂度 \(\Theta(n)\)。
Code
#include<bits/stdc++.h>
#define pd push_back
#define pb pop_back
#define mk make_pair
//#define int long long
#define PII pair<int,int>
#define _for(a,b,c) for(int a=b;a<=c;a++)
#define _rep(a,b,c) for(int a=b;a>=c;a--)
#define _go(i,u) for(int i=head[u];i;i=Edge[i].nxt)
using namespace std;
namespace I {
template <typename T> void read(T& x) {
x=0; T f=1; char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch&15); ch=getchar(); }
x=x*f; return;
}
template <typename T,typename ...Arg> void read(T& x,Arg& ...arg) {
read(x); read(arg...);
}
}
namespace Math {
template <typename T> T power(T a,T b,T MOD) {
T ans=1; while(b) { if(b&1) ans=ans*a%MOD; a=a*a%MOD; b>>=1; } return ans%MOD;
}
template <typename T> T gcd(T a,T b) {
if(b) return gcd(b,a%b); return a;
}
template <typename T> T lcm(T a,T b) {
return a/gcd(a,b)*b;
}
template <typename T> T inv(T n,T p) {
return power(n,p-2);
}
const int Test[]={0,2,3,5,7,11,13,17,19,23,29};
template <typename T> bool is_prime(T n) {
if(n<2) return 0;
T t=n-1,k=0; while((t+1)&1) ++k,t>>=1;
_for(i,1,10) {
if(n==Test[i]) return 1;
T s=power(Test[i],t,n); T tmp=s;
_for(j,1,k) {
tmp=s*s%n; if(tmp==1&&s!=1&&s!=n-1) return 0; s=tmp;
}
if(s!=1) return 0;
}
return 1;
}
}
const int N=2e5+5;
char s[N];
int T,n,pre0[N],pre1[N],nxt0[N],nxt1[N];
signed main() {
I::read(T);
while(T--) {
scanf("%s",s+1); n=strlen(s+1);
_for(i,1,n) pre0[i]=pre0[i-1]+(s[i]=='0'),pre1[i]=pre1[i-1]+(s[i]=='1');
_for(i,1,n) nxt0[i]=nxt0[i-1]+(s[n-i+1]=='0'),nxt1[i]=nxt1[i-1]+(s[n-i+1]=='1');
int ans=0x7fffffff;
_for(i,0,pre0[n])
ans=min(ans,min(pre0[n]-pre0[i]-nxt0[pre0[n]-i],pre1[i]+nxt1[pre0[n]-i+1]));
printf("%d\n",ans);
}
return 0;
}

浙公网安备 33010602011771号