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;
}
posted @ 2022-08-01 12:03  lsj2009  阅读(33)  评论(0)    收藏  举报