2023省选模拟(沈队)

A. 游戏

首先可以去掉两侧数都比他大的,然后得到一个单峰的序列

然后基本上是从峰到两侧,但是可能有点细节,我直接按照两个关键字(代价和本身权值)做的

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 5e5 + 55;
int n, a[maxn], val[maxn]; ll ans;
struct node{
	int val, myval, id;
	node(){}
	node(int _a, int _b, int _c){val = _a; myval = _b; id = _c;}
	friend bool operator < (const node &x, const node &y){
		return x.val == y.val ? (x.myval == y.myval ? x.id < y.id : x.myval < y.myval) : x.val > y.val;
	}	
};
set<node>s; set<int>pos;
int getval(int x){
	auto it = pos.lower_bound(x);
	return min(a[*prev(it)], a[*next(it)]);
}
vector<int>sl, sr;
void solve(){
	pos.insert(0); pos.insert(n + 1);
	for(int i : sl)pos.insert(i);
	for(int i : sl){
		val[i] = getval(i);
		s.insert(node(val[i], a[i], i));
	}
	while(s.size() > 2){
		node now = *s.begin(); s.erase(now);
		ans += now.val;
		auto it = pos.lower_bound(now.id); 
		int x = *prev(it), y = *next(it); pos.erase(it);
		s.erase(s.lower_bound(node(val[x], a[x], x)));
		s.erase(s.lower_bound(node(val[y], a[y], y)));
		val[x] = getval(x); val[y] = getval(y);
		s.insert(node(val[x], a[x], x));
		s.insert(node(val[y], a[y], y));
	}
}
int main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	n = read(); for(int i = 1; i <= n; ++i)a[i] = read();
	int mxp = 1; for(int i = 1; i <= n; ++i)if(a[i] > a[mxp])mxp = i;
	for(int i = mxp; i >= 1; --i){
		while(sl.size() && a[i] > a[sl.back()]){sl.pop_back(); ans += min(a[i], a[sl.back()]);}
		sl.push_back(i);	
	}
	for(int i = mxp; i <= n; ++i){
		while(sr.size() && a[i] > a[sr.back()]){sr.pop_back(); ans += min(a[i], a[sr.back()]);}
		sr.push_back(i);
	}
	for(int v : sr)if(v != mxp)sl.push_back(v);
	solve();
	// sr.pop_back();
	// while(sl.size() > 1 && sr.size() > 1){
	// 	int vl = min(sr.back(), sl[sl.size() - 2]);
	// 	int vr = min(sl.back(), sr[sr.size() - 2]);
	// 	if(vl > vr){ans += vl; sl.pop_back();
	// 	}else{ans += vr; sr.pop_back();}
	// }
	printf("%lld\n",ans);
	return 0;
}

B. 专家

初始的图(散点没画)

image

三元环 (1,2,3) 贡献 6

现在考虑处理 k

下面的 411 的链盘起来连上,先考虑他们和环上的连边,那么每个点有两个可能的颜色,写出来就是

(2,3)(3,1)(1,2)(2,3)...

发现一个点选择了右侧的颜色,后面点的颜色就是固定的

如果在他们下面挂上点,并把挂上的点向他们左侧颜色连边,那么只有他取到左颜色时会有 2 的贡献

i 下面挂了 ai 个点,那么可以得到 1+2a1+2a1+a2+2a1+a2+a3...

于是可以二进制拆分 k 解决问题, 411 恰好 8 个点就是这个原因

计算每个 1 对应二的幂次,对其差分,按顺序挂上差分后点的数量,消去后面多余的链的贡献直接向左颜色连边

无用点随便找两个不同颜色连上

注意我们只能处理奇数,但是随便挂一个点就能得到 2 的系数,于是挂后导 0 个点在随便一个颜色的点下面即可

code
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
vector<int>rem;
vector<pii>edge;
int leftcolor(int k){return k % 3 + 1;}
void report(int u, int v){edge.push_back(pii(u, v));}
void print(){
	printf("%d\n",(int)edge.size());
	for(pii v : edge)printf("%d %d\n",v.first, v.second);
}
int main(){
	printf("19 ");
	report(1, 2); report(1, 3); report(2, 3); report(1 ,4);
	for(int i = 5; i <= 11; ++i)report(i - 1, i), report(i, i % 3 ? i % 3 : 3);
	print();
	for(int i = 1; i <= 500; ++i){
		int cnt = 0, tmp = i, id = 12; rem.clear(); edge.clear();
		while(tmp % 2 == 0)tmp /= 2, ++cnt;
		for(int j = 0; j < cnt; ++j)report(19 - j, 1);
		tmp -= 1; while(tmp){int j = (tmp & -tmp); rem.push_back(__lg(j)); tmp -= j;}
		for(int j = rem.size() - 1; j >= 1; --j)rem[j] -= rem[j - 1];
		for(int j = 0; j < rem.size(); ++j)for(int k = 1; k <= rem[j]; ++k)
			report(id, j + 4), report(id, leftcolor(j + 4)), ++id;
		if(rem.size() < 8)report(4 + rem.size(), leftcolor(4 + rem.size()));
		for(int j = id; j <= 19 - cnt; ++j)report(1, j), report(2, j);
		print();
	}
	return 0;
}

C. 平衡

咕咕咕

code

posted @   Chen_jr  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示