Codeforces Round #809 (Div. 2)

Codeforces Round #809 (Div. 2)

A .Another String Minimization Problem

很自然想到,枚举数组取

t = m i n ( a [ i ] , m + 1 − a [ i ] ) t = min( a[i] , m+1 -a[i] ) t=min(a[i],m+1a[i])

k = m a x ( a [ i ] , m + 1 − a [ i ] ) k = max(a[i],m+1-a[i]) k=max(a[i],m+1a[i])

s [ t ] s[t] s[t] 不为 A A A, 变为 A A A 并标记

s [ t ] s[t] s[t] 被标记,则把 s [ k ] s[k] s[k] 变为 A A A 并标记

void solve(){
    int n,m;
    cin>>n>>m;
    vector<int> a(n+1);
    forr(i,1,n) cin >> a[i];
    string s;
    forr(i,1,m) s+="B";
    //cout << s << endl;
    map<int,int> mp;
    forr(i,1,n){
        int t = min(a[i],m+1-a[i]);
        int k = max(a[i],m+1-a[i]);
        if(mp[t-1]){
            s[k-1] = 'A';
            mp[k-1]++;
        }
        else{
            mp[t-1]++;
            s[t-1] = 'A';
        }
        //cout << s << endl;
    }
    cout << s << endl;
}

B .Making Towers

找规律

对于数 i i i , 当数 i i i 之间间隔为偶数时 , a n s [ i ] + + ans[i]++ ans[i]++;

vector<int> g[100005];
void solve(){
    int n;cin>>n;
    vector<int> a(n+1);
    set<int> s;
    forr(i,1,n) cin >> a[i],g[a[i]].push_back(i),s.insert(a[i]);
    map<int,int> mp;
    for(auto it = s.begin();it!=s.end();it++){
        int t = *it;
        mp[t] = 1;
        for(int i = 1;i < g[t].size();i++){
            int tt = g[t][i] - g[t][i-1] - 1;
            if( (tt%2)==0) mp[t]++;
        }
    }
    forr(i,1,n) cout << mp[i] <<" \n"[i==n];
    for(auto it = s.begin();it!=s.end();it++) g[*it].clear();
}

C .Qpwoeirut And The City

先要求峰最多再要求添加最少

奇数答案一定隔一个判断一次,贡献一次答案

考虑偶数

​ 偶数在满足峰最多的前提,有一次可以隔两个选并且只有一个位置,否则不可能取到最多峰

​ 那么枚举哪个位置隔二个,很多题解都是通过两个前缀来做

​ 官方 tutorial 方法更容易理解

请添加图片描述

int a[100005];
int cal(int x){
    return max(0ll,(max(a[x-1],a[x+1])+1-a[x]));
}

void solve(){
    int n;
    cin >> n;
    forr(i,1,n) cin >> a[i];
    int res = 0;
    if(n&1){
        for(int i = 2;i <= n;i+=2){
            if(a[i] > a[i-1] && a[i] > a[i+1]) continue;
            else {
                res += max(a[i-1],a[i+1]) + 1-a[i];
            }
        }
            cout << res << endl;
    }
    else{
            for(int i = 2; i < n;i+=2){
                if(a[i] > a[i-1] && a[i] > a[i+1]) continue;
                else {
                    res += max(a[i-1],a[i+1]) + 1-a[i];
                }                
            }
            int sum = res;
            for(int i = n-1;i>1;i-=2){
                res -= cal(i-1);
                res += cal(i);
                sum = min(res,sum);
            }
            cout << sum << endl;
        }
}

D1 .Chopping Carrots (Easy Version)

$n <= 3000 $

考虑 min ⁡ 1 < = i < = n ( ⌊ a i p i ⌋ )   =   v \min\limits_{1<=i<=n}(\lfloor \frac{a_i}{p_i} \rfloor) \ = \ v 1<=i<=nmin(⌊piai⌋) = v

分析可知 v ∈ [ 0 , a 1 ] v ∈ [0,a_1] v[0,a1]

反证

​ 若 v ∈ [ a 2 , a n ] v ∈ [a_2,a_n] v[a2,an] 因为 min ⁡ 1 < = i < = n ( ⌊ a i p i ⌋ )   =   v \min\limits_{1<=i<=n}(\lfloor \frac{a_i}{p_i} \rfloor) \ = \ v 1<=i<=nmin(⌊piai⌋) = v , 那么对于 x   =   [ a 1 , v − 1 ] x \ =\ [a_1,v-1] x = [a1,v1] , 总有 x p i < v \frac{x}{p_i} < v pix<v

v ∈ [ 0 , a 1 ] v ∈ [0,a_1] v[0,a1]

那么枚举 v v v, 计算出 p i = m i n ( k , a i / v ) p_i = min(k,a_i/v) pi=min(k,ai/v) ,再用计算出的 p i p_i pi 求出 max ⁡ 1 < = i < = n ( ⌊ a i p i ⌋ ) \max\limits_{1<=i<=n}(\lfloor \frac{a_i}{p_i} \rfloor) 1<=i<=nmax(⌊piai⌋) min ⁡ 1 < = i < = n ( ⌊ a i p i ⌋ ) \min\limits_{1<=i<=n}(\lfloor \frac{a_i}{p_i} \rfloor) 1<=i<=nmin(⌊piai⌋)

做差求最大的差值 r e s res res

O ( n ∗ a i ) O(n*a_i) O(nai)

void solve(){
    int n,k;
    cin>>n>>k;
    vector<int> a(n+1);
    forr(i,1,n) cin >> a[i];
    int mx = -inf,mi = inf;
    int res = inf;
    for(int v = 0;v <= a[1];v++){
        int p;
        forr(i,1,n){
            if(!v) p = k;
            else p = min(k,a[i]/v);
            mx = max(mx,a[i]/p);
            mi = min(mi,a[i]/p);
        }
        // cout << mx << " " << mi << endl;
        // cout << res << endl;
        res = min(res,mx-mi);
        mx = -inf,mi = inf;
    }
    cout << res << endl;
}

E .Qpwoeirut and Vertices

Kruskal 重构树

给一个区间 [ l , r ] [l,r] [l,r]

要求的就是这个区间的子区间 ( [ a , b ]     l < = a < = b < = r [a,b]\,\ l<=a<=b<=r [a,b] l<=a<=b<=r a → b a\rightarrow b ab 的所有路径最长边权的最小值 )的最大值

[ a , b ]     l < = a < = b < = r [a,b]\,\ l<=a<=b<=r [a,b] l<=a<=b<=r a → b a\rightarrow b ab 的所有路径最长边权的最小值 ,可以用Kruskal重构树直接求出

然后就是对 [l,r] 的一次区间求和, 因为是静态的可使用 S T 表 ST表 ST O ( n ) O(n) O(n) 预处理

注意各种数组的初始化问题

int n, m, q;
struct node
{
    int u, v, w;
    bool operator<(const node &t) const
    {
        return w < t.w;
    }
} edge[400005];
struct nod
{
    int to, ne;
} e[400005<<1];
int h[400005<<1], cnt = 1;
void add(int u, int v)
{
    e[++cnt] = {v, h[u]};
    h[u] = cnt;
}
int pre[400005<<1];
int tot;
int val[400005];
int d[400005<<1];
int f[200005][21]; 

int find(int x)
{
    return x == pre[x] ? x : pre[x] = find(pre[x]);
}

void dfs(int u, int fa)
{
    d[u] = d[fa] + 1;
    // cout <<u <<" "<<  d[u] << endl;
    for (int i = h[u]; i; i = e[i].ne)
    {
        int v = e[i].to;
        if (v == fa)
            continue;
        f[v][0] = u;
        forr(k, 1, 20) 
            if (f[v][k - 1])
                f[v][k] = f[f[v][k - 1]][k - 1];
            else 
                break;
        dfs(v, u);
    }
}

int lca(int a, int b)
{
    if (d[a] > d[b])
        swap(a, b);
    for (int i = 20; i >= 0; i--)
        if (d[f[b][i]] >= d[a])
            b = f[b][i];
    if (a == b)
        return a;
    for (int i = 20; i >= 0; i--)
        if (f[a][i] != f[b][i])
            a = f[a][i], b = f[b][i];
    return f[a][0];
}

void kruskal()
{
    sort(edge + 1, edge + 1 + m);
    forr(i, 1, n) pre[i] = i,d[i] = 0;
    tot = n;
    forr(i, 1, m)
    {
        int a = find(edge[i].u), b = find(edge[i].v);
        if (a != b)
        {
            val[++tot] = edge[i].w;
            d[tot] = 0;
            pre[tot] = pre[a] = pre[b] = tot;
            add(tot, a);
            add(a, tot);
            add(b, tot);
            add(tot, b);
        }
    }
    for (int i = tot; i >= 1; i--)
    {
        if (!d[i]){
            dfs(i, -1);
        }
    }
}

int dis[200005];
int ff[200005][21];
void ST(){
    // 不用初始化
    forr(i,1,n-1) ff[i][0] = dis[i];
    forr(i,1,20)for(int j = 1;j+(1<<i)-1 <= n-1;j++){
        ff[j][i] = max(ff[j][i-1],ff[j+(1<<(i-1))][i-1]);
    }
}


int query(int l,int r){
	int s = __lg(r-l+1);
	return max(ff[l][s],ff[r-(1<<s)+1][s]);
}
void solve()
{

    cin >> n >> m >> q;
    cnt = 1;
    forr(i,1,2*n) h[i] = 0;
    forr(i,1,2*n) forr(j,1,20) f[i][j] = 0;
    cnt = 1;
    forr(i, 1, m)
    {
        int u, v;
        cin >> u >> v;
        edge[i] = {u, v, i};
    }
    kruskal();

    for(int i = 1;i<n;i++){
        int t = lca(i,i+1);
        dis[i] = val[t];
    }
    ST();
    while (q--)
    {
        int l, r;
        cin >> l >> r;
        if(l==r) cout << 0 <<" ";
        else cout << query(l,r-1) << " ";
    }
    cout << endl;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值