Give us a try to sing a rhyme
极其困难与精妙的题目。
先考虑区间 \([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;
}

浙公网安备 33010602011771号