Codeforces Round 890 (Div. 2)

Codeforces Round 890 (Div. 2)

 

A. Tales of a Sort

题意:

​ 有一个数组,每次操作可以让所有数减一,为零则不变,问最少多少次操作可使数组变有序

Idea:

​ 答案即为 m a x ( a i ∣ i ∈ [ 1 , n − 1 ] , a i > a i + 1 ) max(a_i|i\in [1,n-1], a_i>a_{i+1}) max(aii[1,n1],ai>ai+1)

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int n;
int a[N], m, b[N];

void solve(){
    cin >> n;
    for(int i = 1;i <= n;i++ ) cin >> a[i];
    int ans = 0;
    for(int i = 1;i < n;i++){
        if(a[i] > a[i+1]){
            ans = max(ans, a[i]);
        }
    }

    cout << ans << '\n';

}

signed main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int t = 1;
    cin >> t;
    while(t--) solve();

    return 0;
}

 

B. Good Arrays

题意:

​ 给出一个正整数(元素均大于0)数组a,问是否存在正整数数组b,且数组b的所有元素之和与a相同,但 a i ! = b i a_i!=b_i ai!=bi

Idea:

​ 将a数组中的非1元素全换为1,为1的就加1,剩下的数随便加到一个数上,判断是否能按该条件构造即可,n == 1 再特判一下

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 10;
int n;
int a[N];

void solve(){
    cin >> n;
    for(int i = 1;i <= n;i++ ) cin >> a[i];
    
    if(n == 1){
        cout << "NO\n";
        return;
    }
    
    int ned = 0, sum = 0;
    for(int i = 1;i <= n;i++){
        if(a[i] == 1) ned++;
        else sum += a[i] - 1;
    }
    cout << (ned <= sum ? "YES\n" : "NO\n");
}

signed main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int t = 1;
    cin >> t;
    while(t--) solve();


    return 0;
}

 

C. To Become Max

题意:

​ 给一个数组a,可进行操作选择 i , 1 ≤ i ≤ n − 1 i,1 \le i \le n-1 i,1in1 a i ≤ a i + 1 a_i \le a_{i+1} aiai+1,然后让 a i a_i ai 加一,最多进行k次操作,求操作后数组中最大值最多可达到多大

Idea:

​ 数组长度最多只有1000,可以很暴力。

​ 思考操作完的情况,如果 a i a_i ai是最后数组中的最大值,那我们一定没有选择在 a i a_i ai前面的数上操作,因为没有收益,最多只会在 a i a_i ai后面操作,因为 a i ≤ a i + 1 + 1 a_i \le a_{i+1}+1 aiai+1+1。所以枚举每个数,再二分判断这个数最大可以达到多大即可

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1010;
int n, k;
int a[N];

bool check(int i, int x){
    int cnt = x, ne = a[i] + x - 1;
    for(int j = i + 1;j <= n;j++, ne--){
        if(a[j] >= ne) break;
        if(j == n) return false; //最后一个数不能增加
        cnt += ne - a[j];
    }
    return cnt <= k;
}

void solve(){
    cin >> n >> k;
    for(int i = 1;i <= n;i++ ) cin >> a[i];

    int ans = a[n];
    for(int i = 1;i < n;i++){
        int l = 1, r = k;
        while(l <= r){
            int mid = l + r >> 1;
            if(check(i, mid)) l = mid + 1;
            else r = mid - 1;
        }
        ans = max(ans, a[i] + l - 1);
    }

    cout << ans << '\n';
}

signed main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int t = 1;
    cin >> t;
    while(t--) solve();

    return 0;
}

 

D. More Wrong

题意:

​ 交互题。有一个隐藏的排列,每次可询问一段区间 [ l , r ] [l,r] [l,r]的逆序对数量,花费是 ( r − l ) 2 (r-l)^2 (rl)2,在花费不超过 5 n 2 5n^2 5n2的情况下求出数组中最大值的下标

Idea:

​ 考虑两个相邻的数,若他们之间有一个逆序对则后一个数小,前一个数大,可如此两两询问就获得了 n / 2 n/2 n/2个可能作为答案的数。再对这 n / 2 n/2 n/2个数两两询问。

​ 此时区间大小不是2了,如何判断两个数中那个较大?

​ 对于每两个数,分别询问 [ l , r ] [l,r] [l,r] [ l , r − 1 ] [l,r-1] [l,r1],判断两次获得的值是否相同,相同则说明去掉右端点对该区间的逆序对数量没有影响,说明右端点为该区间最大值。

​ 如此分治询问,形如一棵线段树,算了一下花费应该不会超过 4 n 2 4n^2 4n2

code:

#include<bits/stdc++.h>
using namespace std;
int n, ret;

void ask(int l, int r){
    if(l >= r){
        ret = 0;
        return;
    }
    cout << "? " << l << ' ' << r << '\n';
    fflush(stdout);
    cin >> ret;
}

void solve(){
    cin >> n;
    queue<int>q;
    for(int i = 1;i <= n;i++) q.push(i);
    while(q.size() > 1){
        int l = q.front();
        q.pop();
        int r = q.front();
        q.pop();
		
        //若拿到尾部和头部端点,就把尾部放回去,重拿一个,否则代价会超出
        if(l > r){  
            q.push(l);
            l = r;
            r = q.front();
            q.pop();
        }

        ask(l, r);
        if(ret == -1) return;
        int s1 = ret;
        ask(l, r - 1);
        int s2 = ret;

        if(s1 == s2){    //右端点大
            q.push(r);
        }else{
            q.push(l);
        }
    }
    cout << "! " << q.front() << '\n';
}

int main(){

    int t;
    cin >> t;
    while(t--) solve();

    return 0;
}

 

E1. PermuTree (easy version)

题意:

​ 给出一棵根为1,有n个节点的树,每个节点的值为排列a,最多能有多少对 u , v u,v u,v 使得 a u < a l c a ( u , v ) < a v a_u < a_{lca(u,v)} < a_v au<alca(u,v)<av

Idea:

​ 树形dp, d p [ i ] dp[i] dp[i]表示以 i 为根的子树能产生多少对贡献。

d p i dp_i dpi 首先从其儿子直接加过来,然后考虑以该点为lca产生的贡献。将子树和分成差值最小的两份,两份相乘即为新产生的最大贡献。均分子树和这个操作直接用bitset优化枚举所有子树和的情况即可

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5010;
int n, dp[N], siz[N];
vector<int>g[N];

void dfs(int u, int fa){
    siz[u] = 1;
    bitset<N>bt;
    bt.set(0);
    for(auto v : g[u]) if(v != fa){
        dfs(v, u);
        siz[u] += siz[v];
        dp[u] += dp[v];
        bt |= (bt << siz[v]);
    }
    int tmp = 0;
    for(int s = 1;s <= n / 2;s++){
        if(bt[s])
            tmp = max(tmp, s * (siz[u] - 1 - s));
    }
    dp[u] += tmp;
}

signed main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n;
    for(int i = 2;i <= n;i++){
        int p;
        cin >> p;
        g[i].push_back(p);
        g[p].push_back(i);
    }

    dfs(1, 0);

    cout << dp[1] << '\n';

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BowTen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值