ARC126 A-D

ARC126 A-D

A.

题意

(N_2)个2,(N_3)个3,(N_4)个4,可以拼凑出多少个10

[0leq N_1,N_2,N_3 leq10^{15} ]

分析

枚举可以发现,凑出10的方案只有以下:

[ 2 imes 5\ 2 imes 3 + 4 imes1\ 2 imes 2 + 3 imes 2\ 2 imes 1 + 4 imes 2\ 3 imes 2 + 4 imes 1 ]

显然,对于这些方案,贪心方法一定是一直用某种方案直到不能用了,因此只需要枚举这些方案的顺序对答案取max即可

代码

int main(){
    int T = rd();
    while(T--){
        ll N1 = rd();
        ll N2 = rd();
        ll N3 = rd();
        vector<int> op(5);
        for(int i = 0;i < 5;i++)
            op[i] = i;
        ll res = -1;
        do{
            ll n2 = N1;
            ll n4 = N3;
            ll n3 = N2;
            ll mx;
            ll ans = 0;
            for(int i = 0;i < 5;i++){
                if(!op[i]) {
                    mx = n2 / 5;
                    n2 -= mx * 5;
                    ans += mx;
                }
                else if(op[i] == 1){
                    mx = min(n2 / 2,n3 / 2);
                    n2 -= mx * 2;
                    n3 -= mx * 2;
                    ans += mx;
                }
                else if(op[i] == 2){
                    mx = min(n2 / 3,n4);
                    n2 -= mx * 3;
                    n4 -= mx;
                    ans += mx;
                }
                else if(op[i] == 3){
                    mx = min(n2,n4 / 2);
                    n2 -= mx;
                    n4 -= mx * 2;
                    ans += mx;
                }
                else {
                    mx = min(n3 / 2,n4);
                    n3 -= mx * 2;
                    n4 -= mx;
                    ans += mx;
                }
            }
            res = max(res,ans);
        }while(next_permutation(op.begin(),op.end()));
        printf("%lld
",res);
    }
}

B.

题意

给出(2N)个点,分别是((1-N,0),(1-N,1))

给出(M)条边,第(i)条连接((a_i,0))((b_i,1))

(M)条中选出最大的个数,使得边在平面上不相交(也不能有相同endpoint)

[1 leq N,Mleq2 imes 10^5\ a_i eq a_j or b_i eq b_j ]

分析

固定一维,显然这个时候就是要求另一维的最长上升子序列,(否则一定有交),如果这一维不存在(a_i = a_j)的话显然就直接可以做了,但是题目只确保不重边

这个时候只需规定(a_i = a_j)时,若(b_i > b_j)(i)排在(j)的前面即可,这样就避免了求(LIS)时同时选到同一个点,且对其他点无影响

剩下只要求(O(nlogn))(LIS)即可

代码

inline bool cmp(pair<int,int> a,pair<int,int> b){
    if(a.fi == b.fi) return a.se > b.se;
    return a.fi < b.fi;
}

int main(){
    int n = rd();
    int m = rd();
    vector<pair<int,int>> v(m);
    for(int i = 0;i < m;i++)
        v[i].fi = rd(),v[i].se = rd();
    sort(v.begin(),v.end(),cmp);
    VI tmp;
    for(int i = 0;i < m;i++){
        if(tmp.empty() || tmp.back() < v[i].se) tmp.push_back(v[i].se);
        else *lower_bound(tmp.begin(),tmp.end(),v[i].se) = v[i].se;
    }
    cout << (int)tmp.size();
}

C.

题意

给定数组(A),可以进行至多(k)次操作,每次操作选择(A)中某个数+1,找出操作后最大的(gcd)

[2 leq N leq 3 imes 10^5\ 1 leq K leq 10^{18}\ 1 leq A_i leq 3 imes 10^5 ]

分析

求最大的(gcd),可以等价为求最大的某个数(x),使得(x|a_i,forall i in [1,n])

(k)非常大时,我们肯定希望每个数的大小都相同,如果最后的答案要大于(a_i),只需把所有数加到答案

如果最后答案等于已经存在(a_i),我们发现可以较快的计算出所需的操作次数

考虑答案为(x)(a_i)变为的最小操作次数即找到某个(k),使得((k -1)x leq a_i leq k x),次数(kx - a_i)

答案即(sum (kx - a_i) = sum cnt - sum a_i)(sum cnt)可以用一个前缀桶存储出现次数来维护,枚举(x)和枚举(k),复杂度显然就是调和级数

代码

int main(){
    int n = rd();
    ll k = rd();
    VI v(n + 1);
    ll tot = 0;
    for(int i = 1;i <= n;i++)
        v[i] = rd(),tot += v[i];
    ll mx = *max_element(v.begin() + 1,v.end());
    if(k >= mx * n - tot) {
        printf("%lld
",(k + tot) / n);
        return 0;
    }
    VI cnt(2 * mx + 1);
    for(int i = 1;i <= n;i++)
        cnt[v[i]]++;
    for(int i = 1;i <= mx * 2;i++)
        cnt[i] = cnt[i] + cnt[i - 1];
    for(int x = mx;x >= 1;x--){
        ll cost = 0;
        for(int l = 1;l <= mx;l += x){
            cost += (cnt[l + x - 1] - cnt[l - 1]) * (l + x - 1);
        }
        cost -= tot;
        if(cost <= k) {
            printf("%d
",x);
            return 0;
        }
    }
}

D.

题意

给定(N)个值域([1,k])的数,每次可以交换距离为(1)的数,求最小的次数使得存在子区间([1,2,...k-1,k])

[2 leq K leq 16\ K leq N leq 200 ]

分析

看数据范围容易想到是状压DP,(dp[i][S])表示前(i)个数,已经凑出状态(S)的最小交换次数

显然答案由两部分组成:逆序对个数和组成答案的数到最终区间的位置

考虑转移:加入一个数时,从前向后加入某个数时,显然要加上比它大的个数(逆序对)。考虑位置,每次要到某个区间,要么已经存在的数要整体移动一个位置,要么还没加入的数要整体移动一个位置,对两者取min

代码

int main(){
    int n = rd();
    int k = rd();
    VI dp(1 << k,1e9);
    dp[0] = 0;
    for(int i = 0;i < n;i++){
        int a = rd();
        a--;
        for(int j = (1 << k ) - 1;j >= 0;j--){
            if(dp[j] != 1e9) {
                if(!((j >> a) & 1)) 
                    dp[j | 1 << a] = min(dp[j | 1 << a],dp[j] + __builtin_popcount(j & ~((1 << a) - 1)));
                dp[j] += min(__builtin_popcount(j),__builtin_popcount(j ^ (1 << k) - 1));
            }
        }
    }
    printf("%lld",dp[(1 << k) - 1]);
}
原文地址:https://www.cnblogs.com/hznumqf/p/15321424.html