Codeforces Round #809 (Div. 2)

Codeforces Round #809 (Div. 2)

C(贪心)

题意

有一排楼,要往上建,认为如果一座楼比它左右都高,那么就是一座酷楼。在保证这种楼最多的情况,求最少往上盖。

思路

很容易想到最多就是隔一个建一个酷楼,就是 0101010 的形式。这在总楼数为奇数的情况下是符合的。

当楼数为偶数,允许一次中间隔连续隔两个楼建酷楼。这相当于对上面的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(整除)

题意

给一堆数 a ,任意构造一个序列 p 求这个的最小值

max1in(aipi)min1in(aipi)

思路

数据量比较小,枚举可以接受。

最小值范围是 [0,amin] 考虑确定最小值 min 后,max 的贡献。

贪心考虑,max 尽可能小,且满足 aipimin 。变形后,piminai 。我们希望 pi 尽可能大,整除分块告诉我们, aimin 就是 pi 的最大值。我们考虑所有 ai 找出 max 计算答案即可。


顺便说一下整除分块小结论:k=xy  xk+1<yxk

#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 种 ai 范围拉到 1e5 ,且空间只有 64MB

不考虑空间的话,我们直接把 ai 整除处理出来之后对 min 二分或者维护双指针都可以得到答案,但处理 ai 整除的时空复杂度 O(nn) 就会非常惨的 MLE了。

考虑对 ai 求出所有可行商:s1,s2,s3,...,st

按D1我们确定一个 min ,把求出的可行商放在一个 “值域-i” 的二维坐标系下,取 min 就是类似扫描线一样在该坐标系下画一条竖线。

对每个 ai 的最大值 maxi 贡献分析:当 sj<min<sj+1 时,maxi=sj+1 ,特别的当 j=t 时不合法,当 min<s1maxi=aik

于是我们可以维护一个 top[i] 数组表示最小值为 i 时的 max 。这里 max=max(max1,2,3,...,i,...,n) 。答案就是 min(top[i]i)

在枚举求可行商时,因为 [sj,sj+1] 种的最小值取值情况是一样的,因此只要单点维护 top[sj+1]=max(top[sj+1],sj+1) 即可,而不用对整个区间维护。在将 a1,a2,a3,...,an 全枚举完后取前缀 max 即可完成对未处理部分的更新。

#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;
}

枚举 max 似乎可以做到更优的复杂度,之后再看看。

E(思维,倍增)

题意

n 个点 m 条边的无向联通图,边权为边的编号。

q 次询问,每次询问 [l,r] 内的点互相可达所需要用到的边权最大值的最小值。

思路

很容易想到只有最小生成树上的边有用。

在生成树上考虑,需要观察出一个结论:定义 f[i][i1,i] 的答案,那么 [l,r] 的答案就是 max(f[l+1],f[l+2],...,f[r])


证明:

对区间 [l,r] 的答案为 ki 为区间内一点 ,显然有 kf[i]

考虑是否有 k<f[i] ,假设小于,说明 [i1,i] 可达需要的边比 k 大,不符合 k 定义。

关于如何猜到:询问是一段区间,在和图有关的时候是很少见的,又是一个最大值问题,于是猜测有如上结论。


维护区间最值用st表,求 [i1,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,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;
}
posted @   Mxrurush  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示