P9266 [PA 2022] Nawiasowe podziały
题意
给定一个长度为 \(n\) 的括号串,你现在需要将其分成 \(k\) 部分使得每一部分的合法括号序列子串的总数量最小。
\(k\le n\le 10^5\)
分析
打个表发现有凸性,考虑 wqs 二分。
如果考虑从前往后用栈模拟括号序列,则若 \(l,r\) 两处的栈状态 \(S_i\) 相同,则说明 \((l,r]\) 是一个合法括号序列,那么子区间 \([L,R]\) 的贡献就是 \(\sum_{T}\binom{\sum_{i=L-1}^R [S_i=T]}{2}\)。再打个表发现有决策单调性,套一个二分栈即可,子区间的贡献可以套用 CF868F 的做法用模拟莫队解决,复杂度不知道几个 log,反正至少两个。
不妨换一种方式思考,除了栈我们还可以用折线图来维护括号序列,\(s_i\) 表示横坐标为 \(i\) 时折线图的高度,那么此时子区间 \([L,R]\) 的贡献是 \(\sum_{i=L-1}^R\sum_{j=i+1}^R[\min_{k=i}^js_k=s_i=s_j]\),即满足 \(s_i,s_j\) 均为 \([i,j]\) 最小值的 \((i,j)\) 对数。建出广义笛卡尔树,即每个方点表示一个区间,方点下挂着若干个方点和若干个圆点,每个圆点代表父亲方点区间内的每个最小值,每个方点代表被区间最小值分割出来的子区间。求广义笛卡尔树可以暴力 ST 表挨个查找区间内的最小值及其位置,\(O(n\log n)\)。现在子区间 \([L,R]\) 的贡献就变为了 \(\sum_u\binom{\sum_{v\in \operatorname{son}(u)}[L-1\le v\le R]}{2}\)。
现在考虑做 dp,为了 dp 方便让分为 \(k\) 个部分变为插入 \(k-1\) 个隔板,设 \(f_i\) 为只考虑前 \(i\) 个位置并钦定在 \(i\) 位置上插入一个隔板的最小代价,从儿子向父亲转移时,我们只需要知道其中选没选隔板,再记 \(g_u\) 表示 \(u\) 节点内选了至少一个隔板的最小代价,\(h_u\) 表示 \(u\) 节点内不选隔板的代价(可提前预处理),记 \(pre\) 为圆点个数的前缀和,\(sum\) 为 \(h_u\) 的前缀和,有转移
其中 \(son_i\) 表示该方点的第 \(i\) 个儿子。
发现这坨玩意拆掉之后只有一项 \(-2pre_ipre_{j-1}\) 同时与 \(i,j\) 有关,斜率优化即可。总时间复杂度 \(O(n\log n)\)。
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
#include<unordered_map>
#include<vector>
#include<queue>
#include<stack>
#include<bitset>
#include<set>
#include<array>
#include<tuple>
#include<ctime>
#include<random>
#include<cassert>
#include<chrono>
#define x1 xx1
#define y1 yy1
#define IOS ios::sync_with_stdio(false)
#define ITIE cin.tie(0)
#define OTIE cout.tie(0)
#define PY puts("Yes")
#define PN puts("No")
#define PW puts("-1")
#define P0 puts("0")
#define P__ puts("")
#define PU puts("--------------------")
#define mp make_pair
#define fi first
#define se second
#define getc getchar
#define petc putchar
#define pb emplace_back
#define un using namespace
#define il inline
#define all(x) x.begin(),x.end()
#define mem(x,y) memset(x,y,sizeof x)
#define popc __builtin_popcountll
#define rep(a,b,c) for(int a=(b);a<=(c);++a)
#define per(a,b,c) for(int a=(b);a>=(c);--a)
#define reprange(a,b,c,d) for(int a=(b);a<=(c);a+=(d))
#define perrange(a,b,c,d) for(int a=(b);a>=(c);a-=(d))
#define graph(i,j,k,l) for(int i=k[j];i;i=l[i].nxt)
#define lowbit(x) ((x)&-(x))
#define lson(x) ((x)<<1)
#define rson(x) ((x)<<1|1)
//#define double long double
//#define int long long
//#define int __int128
using namespace std;
using i64=long long;
using u64=unsigned long long;
using pii=pair<int,int>;
template<typename T1,typename T2>inline bool ckmx(T1 &x,T2 y){return x>=y?0:(x=y,1);}
template<typename T1,typename T2>inline bool ckmn(T1 &x,T2 y){return x<=y?0:(x=y,1);}
inline auto rd(){
int qwqx=0,qwqf=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')qwqf=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){qwqx=(qwqx<<1)+(qwqx<<3)+ch-48;ch=getchar();}return qwqx*qwqf;
}
template<typename T>inline void write(T qwqx,char ch='\n'){
if(qwqx<0){qwqx=-qwqx;putchar('-');}
int qwqy=0;static char qwqz[40];
while(qwqx||!qwqy){qwqz[qwqy++]=qwqx%10+48;qwqx/=10;}
while(qwqy--){putchar(qwqz[qwqy]);}if(ch)putchar(ch);
}
bool Mbg;
const int mod=998244353;
template<typename T1,typename T2>inline void adder(T1 &x,T2 y){x+=y,x=x>=mod?x-mod:x;}
template<typename T1,typename T2>inline void suber(T1 &x,T2 y){x-=y,x=x<0?x+mod:x;}
const int maxn=2e5+5,inf=0x3f3f3f3f;
const long long llinf=0x3f3f3f3f3f3f3f3f;
int n,k;
char str[maxn];
int s[maxn];
pii mn[maxn][20];
int lg[maxn];
pii qry(int l,int r){
int p=lg[r-l+1];
return min(mn[l][p],mn[r-(1<<p)+1][p]);
}
int cnt,rt;
int len[maxn];
vector<int>G[maxn];
int build(int l,int r){
int id=++cnt;
G[id].pb(-1);
vector<int>v;
int q=qry(l,r).fi;
for(int p=l;p<=r;){
pii nw=qry(p,r);
if(nw.fi!=q)break;
v.pb(nw.se),p=nw.se+1;
}
if(v[0]>l)G[id].pb(build(l,v[0]-1));
G[id].pb(v[0]);
rep(i,1,(int)v.size()-1){
if(v[i]>v[i-1]+1)G[id].pb(build(v[i-1]+1,v[i]-1));
G[id].pb(v[i]);
}
if(v.back()<r)G[id].pb(build(v.back()+1,r));
len[id]=G[id].size()-1;
return id;
}
//h i子树内不选断点的贡献
i64 h[maxn];
vector<int>pre[maxn];
i64 calc(int x){
return 1ll*x*(x-1)/2;
}
void dfs0(int x){
if(x<=n)return h[x]=0,void();
for(int u:G[x])if(~u)dfs0(u);
pre[x].assign(len[x]+1,0);
rep(i,1,len[x]){
int u=G[x][i];
pre[x][i]=pre[x][i-1];
if(u<=n)pre[x][i]++;
else h[x]+=h[u];
}
h[x]+=calc(pre[x][len[x]]);
}
vector<i64>f;
vector<int>fc;
i64 g[maxn],gc[maxn],sum[maxn];
const __int128 e=1;
struct node{
i64 x,y;int id;
node(i64 _x=0,i64 _y=0,int _id=0){x=_x,y=_y,id=_id;}
friend node operator-(node x,node y){
return node(x.x-y.x,x.y-y.y);
}
friend bool operator<(node x,node y){
if(x.x<0)x.x*=-1,x.y*=-1;
if(y.x<0)y.x*=-1,y.y*=-1;
return e*x.y*y.x<e*x.x*y.y;
}
friend bool operator<=(node x,node y){
if(x.x<0)x.x*=-1,x.y*=-1;
if(y.x<0)y.x*=-1,y.y*=-1;
return e*x.y*y.x<=e*x.x*y.y;
}
friend bool operator>(node x,node y){
if(x.x<0)x.x*=-1,x.y*=-1;
if(y.x<0)y.x*=-1,y.y*=-1;
return e*x.y*y.x>e*x.x*y.y;
}
friend bool operator>=(node x,node y){
if(x.x<0)x.x*=-1,x.y*=-1;
if(y.x<0)y.x*=-1,y.y*=-1;
return e*x.y*y.x>=e*x.x*y.y;
}
};
int hd,tl;
node sta[maxn];
void dfs(int x,i64 K){
if(x<=n)return g[x]=-K,gc[x]=1,void();
for(int u:G[x])if(~u)dfs(u,K);
f.assign(len[x]+1,llinf),fc.assign(len[x]+1,inf);
rep(i,1,len[x])sum[i]=sum[i-1]+h[G[x][i]];
rep(i,1,len[x])f[i]=calc(pre[x][i])+g[G[x][i]]+sum[i-1],fc[i]=gc[G[x][i]];
rep(i,1,1)sta[hd=tl=1]=node(2*pre[x][i-1],2*f[i]+1ll*pre[x][i-1]*pre[x][i-1]+pre[x][i-1]-2*sum[i],i);
rep(i,2,len[x]){
while(hd<tl&&node(1,pre[x][i])>sta[hd+1]-sta[hd])++hd;
const int j=sta[hd].id;const i64 res=f[j]+calc(pre[x][i]-pre[x][j-1])+g[G[x][i]]+sum[i-1]-sum[j];
if(ckmn(f[i],res))fc[i]=inf;
if(f[i]==res)ckmn(fc[i],fc[j]+gc[G[x][i]]);
node nw=node(2*pre[x][i-1],2*f[i]+1ll*pre[x][i-1]*pre[x][i-1]+pre[x][i-1]-2*sum[i],i);
if(sta[tl].x==nw.x){
if(nw.y<sta[tl].y)sta[tl]=nw;
}else{
while(hd<tl&&nw-sta[tl]<sta[tl]-sta[tl-1])--tl;
sta[++tl]=nw;
}
}
g[x]=llinf,gc[x]=inf;
rep(i,1,len[x]){
i64 res=f[i]+calc(pre[x][len[x]]-pre[x][i-1])+sum[len[x]]-sum[i];
if(ckmn(g[x],res))gc[x]=inf;
if(g[x]==res)ckmn(gc[x],fc[i]);
}
}
int solve(i64 K){
dfs(rt,K);
return gc[rt];
}
inline void solve_the_problem(){
n=cnt=rd(),k=rd()-1,scanf("%s",str+1);
rep(i,1,n)s[i]=s[i-1]+1-2*(str[i]-'(');
rep(i,0,n)mn[i][0]=mp(s[i],i);
rep(i,2,n+1)lg[i]=lg[i>>1]+1;
const int M=lg[n+1];
rep(j,1,M)rep(i,0,n-(1<<j)+1)mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
rt=build(0,n);
dfs0(rt);
if(!k)return write(h[rt]);
i64 l=-1e10,r=1e10,res=-1;
while(l<=r){
i64 mid=(l+r)/2;
if(solve(mid)<=k)res=mid,l=mid+1;
else r=mid-1;
}
solve(res);
write(g[rt]+res*k);
}
bool Med;
signed main(){
// freopen(".in","r",stdin);freopen(".out","w",stdout);
fprintf(stderr,"%.3lfMB\n",(&Mbg-&Med)/1048576.0);
int _=1;
while(_--)solve_the_problem();
}
/*
18 18
(()()()))(())(((((
output:0
*/

浙公网安备 33010602011771号