Codeforces Round #809 (Div. 2)
Codeforces Round #809 (Div. 2)
C(贪心)
题意
有一排楼,要往上建,认为如果一座楼比它左右都高,那么就是一座酷楼。在保证这种楼最多的情况,求最少往上盖。
思路
很容易想到最多就是隔一个建一个酷楼,就是 的形式。这在总楼数为奇数的情况下是符合的。
当楼数为偶数,允许一次中间隔连续隔两个楼建酷楼。这相当于对上面的01序列切片,我们处理前后缀后枚举切片点就行。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int n; cin >> n;
vector<int> a(n + 1);
rep(i,1,n) cin >> a[i];
vector<int> pre(n + 1),suf(n + 2);
for(int i = 2;i + 1 <= n;i += 2) {
pre[i] = pre[i - 2] + max(0ll, max(a[i + 1],a[i - 1]) - a[i] + 1);
}
for(int i = n - 1;i > 0;i -= 2) {
suf[i] = suf[i + 2] + max(0ll, max(a[i + 1],a[i - 1]) - a[i] + 1);
}
if(n & 1) cout << pre[n - 1] << endl;
else {
// [1,i][0,0][i + 3,n]
int ans = pre[n - 2];
for(int i = 0;i + 2 <= n;i += 2)
ans = min(ans, pre[i] + suf[i + 3]);
cout << ans << endl;
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
solve();
return 0;
}
D12(整除)
题意
给一堆数 ,任意构造一个序列 求这个的最小值
思路
数据量比较小,枚举可以接受。
最小值范围是 考虑确定最小值 后, 的贡献。
贪心考虑, 尽可能小,且满足 。变形后, 。我们希望 尽可能大,整除分块告诉我们, 就是 的最大值。我们考虑所有 找出 计算答案即可。
顺便说一下整除分块小结论:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int n,k;cin >> n >> k;
vector<int>a(n + 1);
rep(i,1,n) cin >> a[i];
sort(a.begin() + 1,a.end());
int ans = 1e9;
for(int mn = 0;mn <= a[1];mn += 1) {
int mx = -1;
for(int i = 1;i <= n;i += 1) {
// a_i / p_i >= mn
// a_i / mn >= p_i
int p = !mn ? k : min(k, a[i] / mn);
mx = max(mx, a[i] / p);
}
ans = min(mx - mn, ans);
}
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
solve();
return 0;
}
D2 种 范围拉到 ,且空间只有
不考虑空间的话,我们直接把 整除处理出来之后对 二分或者维护双指针都可以得到答案,但处理 整除的时空复杂度 就会非常惨的 MLE了。
考虑对 求出所有可行商: 。
按D1我们确定一个 ,把求出的可行商放在一个 “值域-i” 的二维坐标系下,取 就是类似扫描线一样在该坐标系下画一条竖线。
对每个 的最大值 贡献分析:当 时, ,特别的当 时不合法,当 时 。
于是我们可以维护一个 数组表示最小值为 时的 。这里 。答案就是 。
在枚举求可行商时,因为 种的最小值取值情况是一样的,因此只要单点维护 即可,而不用对整个区间维护。在将 全枚举完后取前缀 即可完成对未处理部分的更新。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
// #define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int n,k; cin >> n >> k;
vector<int> a(n + 1);
rep(i,1,n) cin >> a[i];
if(k == 1) {
cout << a[n] - a[1] << endl;
return;
}
vector<int> top(a[n] + 2);
for(int i = 1;i <= n;i += 1) {
vector<int> s;
for(int j = 1;j <= k;j = 1 + (a[i] / (a[i] / j))) {
s.push_back(a[i] / j);
if(s.back() == 0) break;
}
reverse(s.begin(),s.end());
for(int j = 0;j < s.size();j += 1) {
if(j + 1 < s.size()) top[s[j] + 1] = max(top[s[j] + 1], s[j + 1]);
else top[s[j] + 1] = inf;
}
top[0] = max(a[i] / k, top[0]);
}
rep(i,1,a[1]) top[i] = max(top[i - 1],top[i]);
int ans = inf;
rep(i,1,a[1]) ans = min(ans, top[i] - i);
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
solve();
return 0;
}
枚举 似乎可以做到更优的复杂度,之后再看看。
E(思维,倍增)
题意
个点 条边的无向联通图,边权为边的编号。
次询问,每次询问 内的点互相可达所需要用到的边权最大值的最小值。
思路
很容易想到只有最小生成树上的边有用。
在生成树上考虑,需要观察出一个结论:定义 是 的答案,那么 的答案就是
证明:
对区间 的答案为 , 为区间内一点 ,显然有 。
考虑是否有 ,假设小于,说明 可达需要的边比 大,不符合 定义。
关于如何猜到:询问是一段区间,在和图有关的时候是很少见的,又是一个最大值问题,于是猜测有如上结论。
维护区间最值用st表,求 的答案可以倍增解决。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int n,m,q; cin >> n >> m >> q;
vector<array<int,3>> edge(m + 1);
for(int i = 1;i <= m;i += 1) {
int u,v; cin >> u >> v;
edge[i] = {u,v,i};
}
vector<int> fa(n + 1);
iota(fa.begin(),fa.end(),0);
function<int(int)> root = [&](int x) {
return fa[x] == x ? x : (fa[x] = root(fa[x]));
};
auto getTree = [&]() {
vector<vector<PII>> adj(n + 1);
for(int i = 1;i <= m;i += 1) {
auto [u,v,w] = edge[i];
if(root(u) != root(v)) {
adj[u].push_back({v,w});
adj[v].push_back({u,w});
fa[root(u)] = root(v);
}
}
return adj;
};
auto adj = getTree();
vector<vector<int>> par(n + 1,vector<int>(21));
vector<vector<int>> wig(n + 1,vector<int>(21));
vector<int> dep(n + 1);
function<void(int,int,int)> dfs = [&](int u,int f,int w) {
dep[u] = dep[f] + 1;
par[u][0] = f;
wig[u][0] = w;
for(int j = 1;(1 << j) < dep[u];j += 1){
par[u][j] = par[par[u][j - 1]][j - 1];
wig[u][j] = max(wig[u][j - 1], wig[par[u][j - 1]][j - 1]);
}
for(auto [v,w] : adj[u]) if(v != f) dfs(v,u,w);
};
dfs(1,0,0);
auto getDis = [&](int x,int y) {
if(dep[x] < dep[y]) swap(x,y);
int ans = 0;
for(int j = 20;j >= 0;j -= 1) {
if(dep[par[x][j]] >= dep[y]) {
ans = max(ans, wig[x][j]);
x = par[x][j];
}
}
if(x == y) return ans;
for(int j = 20;j >= 0;j -= 1) {
if(par[x][j] != par[y][j]) {
ans = max(ans,wig[x][j]);
x = par[x][j];
ans = max(ans, wig[y][j]);
y = par[y][j];
}
}
return max({ans, wig[x][0],wig[y][0]});
};
vector<vector<int>> f(n + 1,vector<int>(21));
rep(i,2,n) f[i][0] = getDis(i - 1,i);
for(int j = 1;j < 21;j += 1)
for(int i = 2;i + (1 << j) - 1 <= n;i += 1)
f[i][j] = max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
auto getMax = [&](int l,int r) {
if(l > r) return 0ll;
int k = __lg(r - l + 1);
return max(f[l][k],f[r - (1 << k) + 1][k]);
};
while(q --) {
int l,r; cin >> l >> r;
cout << getMax(l + 1,r) << ' ';
}
cout << endl;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具