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. 专家
初始的图(散点没画)
三元环 贡献
现在考虑处理
下面的 的链盘起来连上,先考虑他们和环上的连边,那么每个点有两个可能的颜色,写出来就是
发现一个点选择了右侧的颜色,后面点的颜色就是固定的
如果在他们下面挂上点,并把挂上的点向他们左侧颜色连边,那么只有他取到左颜色时会有 的贡献
设 下面挂了 个点,那么可以得到
于是可以二进制拆分 解决问题, 恰好 个点就是这个原因
计算每个 对应二的幂次,对其差分,按顺序挂上差分后点的数量,消去后面多余的链的贡献直接向左颜色连边
无用点随便找两个不同颜色连上
注意我们只能处理奇数,但是随便挂一个点就能得到 的系数,于是挂后导 个点在随便一个颜色的点下面即可
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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】