若$s$中存在$)($则总可以扩展为$(A)(B)$,因此最终$s$必然形如$(((\cdots )))$
对括号序列建树,并分析操作的影响——
操作1:选择两个相邻的儿子,将后者及其所有儿子作为前者的儿子
操作2:交换相邻两个儿子,即所有儿子无序
注意到$(((\cdots )))$所对应的树即为一条链,进而操作1需要将所有儿子合并
在此基础上,显然总是从上到下依次处理,这样仅会增加更多的选择,总不劣
具体的,记$T_{i}$为深度为$i$的节点权值集合,则问题转换为以下模型——
初始$S$为空集,$\forall i\in [1,n]$依次执行以下操作:
1.将$T_{i}$加入$S$;2.选择$k\in S$,产生$\begin{cases}(|S|-2)\min_{x\in S}x+k&X=1\\\sum_{x\in S}x-k&Y=1\end{cases}$的代价并删除$k$
注意到$k$恰取遍所有权值,因此与$k$有关的项求和可以直接处理,进而$k$仅代表删除的元素
通常可以贪心删除最大值,但反例在于$|S|=1$时第一项系数为负
当$X=0$时该项不存在,当$X=Y=1$时两项抵消,因此上述贪心均正确
当$X=1$且$Y=0$时,考虑以下做法——
分析$|S|$的形式:对于其第一次递减的位置,在该位置以及之后每一次$|S|$恰减小1
仅考虑$|S|\le 2$的位置,即形如$11\cdots 1\ |\ 22\cdots 2\ |\ 若干\ge 3的元素\ |\ 21$
第1段的选择唯一,可以直接处理,以下不作考虑
第2段中本身无贡献,并枚举结束时$S$中(唯一的)元素$k$,求出对应的值——
第3段中总可以保留极值,进而每一个点的最小值是原最小值与$k$取$\min$,可以差分处理
第4段中$S$初始必然恰包含第3段和$\{k\}$并集的两个极值,进而删除最小值代价即$-最大值$
时间复杂度为$o(n\log n)$(贪心时的set),可以通过
事实上,可以利用$|S|$形式优化掉set,时间复杂度降为$o(n)$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 400005 4 #define M 10000005 5 #define ll long long 6 int n,X,Y,x,a[N],b[N],tot[M],st[N]; 7 char s[N<<1];vector<int>v[N]; 8 namespace Subtask1{ 9 int cnt;ll sum,ans; 10 set<int>S;set<int>::iterator it; 11 void main(){ 12 for(int i=1;i<=n;i++){ 13 for(int j:v[i])cnt++,sum+=b[j],S.insert(j); 14 ans+=sum;if (X)ans+=(ll)(cnt-2)*b[*S.begin()]; 15 x=(*--S.end()),cnt--,sum-=b[x],S.erase(x); 16 } 17 if (!X){ 18 for(int i=1;i<=n;i++)ans-=b[i]; 19 } 20 printf("%lld\n",ans); 21 } 22 }; 23 namespace Subtask2{ 24 int cnt[N],vis[N];ll ans,K[N],B[N]; 25 void main(){ 26 cnt[1]=v[1].size(); 27 for(int i=2;i<=n;i++)cnt[i]=cnt[i-1]+v[i].size()-1; 28 int pos=1; 29 while (cnt[pos]==1)ans-=b[v[pos++][0]]; 30 if (pos<=n){ 31 if (cnt[pos]==2){ 32 vis[v[pos][0]]=vis[v[pos][1]]=1,pos++; 33 while (cnt[pos]==2)vis[v[pos++][0]]=1; 34 } 35 int mn=n+1,mx=0; 36 while (cnt[pos]!=1){ 37 for(int i:v[pos])mn=min(mn,i),mx=max(mx,i); 38 K[mn-1]+=cnt[pos]-2,B[mn]+=(ll)(cnt[pos]-2)*b[mn]; 39 pos++; 40 } 41 for(int i=n;i;i--)K[i]+=K[i+1]; 42 for(int i=mx;i<=n;i++)K[i]--; 43 for(int i=1;i<=n;i++)B[i]+=B[i-1]; 44 for(int i=1;i<mx;i++)B[i]-=b[mx]; 45 ll s=B[n]-b[mx]; 46 for(int i=1;i<=n;i++) 47 if (vis[i])s=min(s,K[i]*b[i]+B[i]); 48 ans+=s; 49 } 50 for(int i=1;i<=n;i++)ans+=b[i]; 51 printf("%lld\n",ans); 52 } 53 }; 54 int main(){ 55 scanf("%d%d%d%s",&n,&X,&Y,s+1); 56 for(int i=1;i<=n;i++)scanf("%d",&a[i]),tot[a[i]]++; 57 for(int i=1;i<M;i++)tot[i]+=tot[i-1]; 58 for(int i=n;i;i--)b[tot[a[i]]]=a[i],a[i]=tot[a[i]]--; 59 for(int i=1,j=0;i<=(n<<1);i++){ 60 if (s[i]=='(')st[++st[0]]=a[++j]; 61 else v[st[0]].push_back(st[st[0]]),st[0]--; 62 } 63 if ((!X)&&(!Y))printf("0\n"); 64 if (Y)Subtask1::main(); 65 if ((X)&&(!Y))Subtask2::main(); 66 return 0; 67 }