[省选联考2022] 序列变换

好题。

考虑先对括号包含关系对括号序列进行构树。

那么考虑实际上一/二操作的实质是:

在同一层中:每次可以选择两个点 \((A,B)\),以 \(val_A * x + val_B * y\) 代价把 \(B\) 丢到下一层,要求每一层只有一个数。

考虑分类讨论:

设本层有 \(m\) 个,最小值是 \(mn\),最大值是 \(mx\) .

\(x = 1,y = 1\)
一个显然的最优方案是:
每个数都会有一次代价,然后每下放一个数都会有一个另外的数的代价,那么答案有:
\((m - 2) * mn + sum\)
即每次留在这层的都是 \(mx\)

\(x = 0,y = 1\)
显然也每次留在这层的是 \(mx\),答案有:\(sum - mx\)

\(x = 1,y = 0\)
听说原本只有这么一种情况的。

考虑一种贪心:每次都往下以最小值的代价传数,最后看保留的是哪一位数:
考虑每个数最终都会到一个位置保留,除了最后一个位置不用付代价外都要付代价
那么自然想到把最大值给留到最后。
但是我们发现这样在只有两个的时候不成立,因为如果传最大值,而不传最小值有可能影响后续取数答案。
那么考虑找到第一个加上上面传下的数量大于\(2\)的位置然后分段,那么从前面来的要么是前缀最大值,要么是前缀最小值,考虑两种情况都跑即可。
小细节:如果有全局最大值,取位置最后的,来保证最小值影响范围大。

点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 400005

char s[N << 1];

int n,x,y;

inline int read(){int x;scanf("%d",&x);return x;}

int now = 0;

using std::vector;

vector<ll>G[N];

using std::multiset;

multiset<ll>S;

ll siz,sum,mn,mx;

#define IT std::set<ll>::iterator

ll ans;

inline void sub1(){
	for(int i = 1;i <= n;++i){ 
		for(auto v : G[i])
		S.insert(v),siz ++ ,sum += v;
		IT it = S.begin();mn = *it;it = S.end();--it;mx = *it;
		ans = ans + sum + (siz - 2) * mn;
		S.erase(it);siz -= 1;sum -= mx;
	}
}

ll MX;

inline void sub2(){
	for(int i = 1;i <= n;++i){
		for(auto v : G[i])
		S.insert(v),siz ++ ,sum += v;
		IT it = S.begin();mn = *it;it = S.end();--it;mx = *it;
		ans = ans + sum - mx;
		S.erase(it);siz -= 1;sum -= mx;
	}
}

int d;

inline ll work(int now){
//	std::cout<<"WORK "<<now<<"\n";
	ll res = 0;
	for(int i = now;i <= n;++i){
		for(auto v : G[i])
		S.insert(v),siz ++ ,sum += v;
		IT it = S.begin();mn = *it;it = S.end();--it;mx = *it;
		if(i >= d){--it,mx = *it;res = res + (siz - 2) * mn + mx;S.erase(it);siz -= 1;sum -= mx;}
		else{res = res + (siz - 2) * mn + mx;S.erase(it);siz -= 1;sum -= mn;}
//		std::cout<<"DEP "<<mx<<" "<<mn<<" "<<res<<"\n";		
	}
	return res; 
}

int flg;

ll SI;

ll MN;

inline void sub3(){
	//nothing haha
	for(int i = 1;i <= n;++i)
	for(auto v : G[i]){if(v > MX)MX = v,d = i;}
	ans = work(1);
	S.clear();sum = siz = 0;
	flg = 0;
	for(int i = 1;i <= n;++i){
		if(G[i].size() != 1 && flg){
			MX = 0;
			MN = *S.begin();
			for(int j = i;j <= n;++j)
			for(auto v : G[j]){if(v > MX)MX = v,d = i;} 			
			ans = std::min(ans,SI - MN + work(i));return ;
		}
		if(G[i].size() == 2)flg = 1;		
		for(auto v : G[i])
		S.insert(v),siz ++ ,sum += v,SI += flg * v;
		IT it = S.end();--it;mx = *it;
		S.erase(it);siz -= 1;sum -= mx;	
	}	
} 

int main(){
//	freopen("bracket.in","r",stdin);
//	freopen("bracket.out","w",stdout);	
	scanf("%d%d%d",&n,&x,&y);
	scanf("%s",s + 1);
	for(int i = 1;i <= n * 2;++i){
		if(s[i] == '(')now ++ ;
		if(s[i] == ')')now -- ;
		if(s[i] == '(')G[now].push_back(read());
	}
	if(x == 1 && y == 1)sub1();
	if(x == 0 && y == 1)sub2();
	if(x == 1 && y == 0)sub3();
	std::cout<<ans<<"\n";
}
posted @ 2022-04-20 21:56  fhq_treap  阅读(118)  评论(0编辑  收藏  举报