Give us a try to sing a rhyme

P9266

极其困难与精妙的题目。

先考虑区间 \([L+1,R]\) 对于答案的贡献,不难发现为 \(\sum\limits_{L\le l < r \le R} [sum_l = sum_r = \min_{i=l}^r (sum_i)]\)。那么我们考虑建立一棵扩展笛卡尔树,有方点表示区间,圆点表示最小值,则方点接着圆点表示一个区间内的最小值,方点接着方点表示一个子区间。则一个方点对答案的贡献为圆点两两组合。

考虑 DP,定义 \(f[u][i]\) 表示 \(u\) 子树,割了 \(i\) 刀,则转移显然。显然 \(i\) 变大,值是单调不升的,因此我们考虑 wqs 二分解决掉 \(k\) 的限制。然后我们意识到这样子不好转移了,因为我们不知道点的具体分割情况。则我们更换状态为:\(f[u]\) 表示 \(u\) 子树,割了至少一刀,\(sum[u]\) 表示一刀不割,\(g[i]\) 表示的是到 \(i\) 子树,恰好割了一刀的 \(min\),\(pre[i]\) 表示的是前缀的圆点个数,则 \(g[i] = \min (g[j]+prs[i-1]-prs[j]+(pre[i]-pre[j])*(pre[i]-pre[j])/2)\)。发现这个东西可以斜率优化,做完了。

时间复杂度单老哥。

Code

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define File(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
#define LL long long
#define fi first
#define se second
const LL INF = 1e11;
const int N = 8e5 + 10;
int n,k;
int a[N], sta[N];
vector<int> G[N];
LL sum[N];
LL C2(const LL &x){
	return x * (x-1) / 2;
}
void init(int u){
	LL cnt = 0;
	for(int v : G[u]){
		if(v > n)
			init(v);
		else cnt ++ ;
		sum[u] += sum[v];
	}
	sum[u] += C2(cnt);
}
struct node{
	LL val,cnt;
	node operator + (const node &b) const{
		return node{val + b.val,cnt + b.cnt};
	}
	bool operator < (const node &b) const{
		if(val == -1) return 0;
		if(b.val == -1) return 1;
		return (val == b.val) ? (cnt < b.cnt) : (val < b.val);
	}
}f[N],g[N];
struct Point{
	LL x,y;
	int cnt;
};
LL prs[N],pre[N];
int dq[N];
LL cost;
LL F(const int &x){
	return g[x].val - prs[x] + C2(pre[x]) + pre[x];
}
LL H(const int &x){
	return pre[x];
}
void solve(int u){
	for(int v : G[u]){
		if(v > n)
			solve(v);
	}
	int len = G[u].size(); len = len * 2 - 1;
	for(int i=1;i<=len;i++){
		g[i] = {0,0};
		pre[i] = prs[i] = 0;
	}
	for(int i=1;i<=len;i++){
		pre[i] += pre[i-1];
		prs[i] += prs[i-1];
		if(i & 1){
			if(G[u][i>>1] <= n) pre[i] ++ ;
			else prs[i] += sum[G[u][i>>1]];
		}
	}
	int hd,tl; hd = tl = 0;
	dq[tl++] = 0;
	f[u] = {-1,0};
	for(int i=1;i<=len;i++){
		node ad;
		if(i & 1){
			if(G[u][i>>1] <= n) continue;
			else if(f[G[u][i>>1]].val == -1) continue;
			else ad = f[G[u][i>>1]];
		}
		else ad = node{cost,1};
		while(hd < tl - 1 && F(dq[hd+1]) - F(dq[hd]) < H(i) * (H(dq[hd+1]) - H(dq[hd])))
			hd ++ ;
		if(hd >= tl)
			g[i] = node{prs[i-1] + C2(pre[i]),0} + ad;
		else{
			int j = dq[hd];
			if(hd < tl - 1 && F(dq[hd+1]) - F(dq[hd]) == H(i) * (H(dq[hd+1]) - H(dq[hd])) && g[dq[hd+1]].cnt < g[dq[hd]].cnt)
				j = dq[hd + 1];
			g[i] = node{prs[i-1] + C2(pre[i]),0} + ad + node{F(j) - H(i) * H(j),g[j].cnt};
		}
		f[u] = min(f[u],g[i] + node{prs[len] - prs[i],0} + node{C2(pre[len] - pre[i]),0});
		while(hd < tl - 1 && (((F(dq[tl-1]) - F(dq[tl-2])) * (H(i) - H(dq[tl-2])) > (F(i) - F(dq[tl-2])) * (H(dq[tl-1]) - H(dq[tl-2])))
			|| (((F(dq[tl-1]) - F(dq[tl-2])) * (H(i) - H(dq[tl-2])) == (F(i) - F(dq[tl-2])) * (H(dq[tl-1]) - H(dq[tl-2]))) && (g[dq[tl-1]].cnt >= g[i].cnt)) ) )
			tl -- ;
		if(hd < tl && H(dq[tl-1]) == H(i)){
			if(F(dq[tl-1]) < F(i)) continue;
			if(F(dq[tl-1]) == F(i)){
				if(H(dq[tl-1]) < H(i)) continue;
				tl -- ;
			}
			else tl -- ;
		}
		dq[tl++] = i;
 	}
	return ;
}
int RT;
bool chk(int rt){
	f[rt] = min(f[rt],node{sum[rt],0});
	return f[rt].cnt <= k - 1;
}
int main(){
	IOS;
	cin >> n >> k;
	a[1] = 0; n ++ ;
	for(int i=2;i<=n;i++){
		char c; cin >> c;
		a[i] = a[i-1] + (c == '(' ? 1 : -1);
	}

	int tot = n;
	for(int i=1;i<=n;i++){
		int tp = sta[0];
		while(tp && a[sta[tp]] > a[i]) tp -- ;
		if(tp == 0){
			a[++tot] = a[i];
			for(int j=1;j<sta[0];j++)
				G[sta[j]].push_back(sta[j+1]);
			if(sta[0]) G[tot].push_back(sta[1]);
			G[tot].push_back(i);
			sta[sta[0] = 1] = tot;
		}
		else if(a[sta[tp]] == a[i]){
			for(int j=tp+1;j<sta[0];j++)
				G[sta[j]].push_back(sta[j+1]);
			if(tp < sta[0])
				G[sta[tp]].push_back(sta[tp+1]);
			G[sta[tp]].push_back(i);
			sta[0] = tp;
		}
		else{
			a[++tot] = a[i];
			for(int j=tp+1;j<sta[0];j++)
				G[sta[j]].push_back(sta[j+1]);
			if(tp < sta[0])
				G[tot].push_back(sta[tp+1]);
			G[tot].push_back(i);
			sta[sta[0]=tp+1] = tot;
		}
	}
	while(sta[0] > 1){
		G[sta[sta[0]-1]].push_back(sta[sta[0]]);
		sta[0] -- ;
	}
	int rt = sta[1];
	init(rt);
	LL l = 0,r = INF,fnd = 0;
	while(l <= r){
		LL mid = l + r >> 1;
		cost = mid;
		solve(rt);
		if(chk(rt)){
			fnd = mid;
			r = mid - 1;
		}
		else l = mid + 1;
	}
	cost = fnd;
	solve(rt);
	cout << f[rt].val - (k-1) * fnd;
	return 0;
}
posted @ 2026-11-01 20:56  WinterXorSnow  阅读(3)  评论(0)    收藏  举报