Codeforces Round 1035(A~D)

A. Add or XOR

给出两个数a,b,我们可以进行若干次以下的操作:
1.花费x,得到\(a=a+1\) ;
2.花费y,得到\(a=a\oplus 1\)
求把a变成b的最小代价,若不能报告-1;

解析:
1.若a>b,那么看看能不能通过一次异或,即是否能使\(a=a-1\)从而等于b,如果可以输出y,反之-1
2.若a<b,检查异或是否能使\(a=a+1\) ,如果可以就选择花费小的使其加一,若不行就直接加一

范围很小直接累加就行

void solve() {
    int a, b, x, y;
    cin >> a >> b >> x >> y;
    if (a > b) {
        if ((a ^ 1) == b) {
            cout << y << '\n';
        }else{
            cout <<-1<<'\n';
        }
    } else {
        int ans = 0;
        while (a != b) {
            if (a & 1) {
                a++;
                ans += x;
            } else {
                a++;
                ans += min(x, y);
            }
        }
        cout <<ans<<'\n';
    }
}

B. Line Segments

给出平面上的两个点p,q的横纵坐标,以及一个长度为n的数组{\(a_i\)},问是否走n步每步长度为\(a_i\),能否从p走到q

很容易想到能走的最长距离是\(\sum{a_i}\) ,现在考虑最短距离,即考虑能否走回原点,最直接的走法就是最长的边向前,其他边都用于往回走,即判断 \(\sum{a_i}-a_{max}\)是否大于\(a_{max}\),如果是一定可以走回原点,如果不是那只能走到 \(\sum{a_i}-2*a_{max}\) ,最后判断|pq|在不在这个范围内即可

void solve() {
    int n, px, py, qx, qy;
    cin >> n >> px >> py >> qx >> qy;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    sort(all(a));
    int mx = 0;
    int mn = 0;
    for (int i = 0; i < n - 1; i++) {
        mx += a[i];
        mn += a[i];
    }
    mx += a[n - 1];
    mn -= a[n - 1];
    if (mn >= 0) mn = 0;
    else mn = abs(mn);
    mx = mx * mx;
    mn = mn * mn;
    int dis = (px - qx) * (px - qx) + (py - qy) * (py - qy);
    if (mn <= dis && dis <= mx) {
        cout << "YES\n";
    } else {
        cout << "NO\n";
    }
}

C. A Good Problem

给定n,l,r,k 构造一个数组{\(a_i\)},满足\(l \le a_i \le r\) ,且各个数字按位异或和等于各个数字按位与和,且在所有合法方案里{\(a_i\)}数组字典序最小,输出\(a_k\)

解析:
1.n为奇数直接构造\(l,l,l,l\)即可
2.n为\(2\)不存在合法数组
3.n为大于2的偶数:先构造\(n-2\)\(l\),此时按位与为\(l\),按位异或为0,在构造\(2\)个一样的数字b,此时按位异或和为0,让按位与为0即可,那么只要b的每一位都与\(l\)与后结果为0即可,所以b的最高位一定比\(l\)大一位,那么剩下的数组全填0最优(不管什么数字和0与都为0),判断这个b在不在范围内即可,最后输出第k个\(a_i\)

注意若使用\((1<<i)\) 请使用\((1LL<<i)\)

void solve() {
    int n, l, r, k;
    cin >> n >> l >> r >> k;
    if (n & 1) {
        cout << l << '\n';
    } else if (n == 2) {
        cout << -1 << '\n';
    } else {
        int mxl = -1;
        for (int i = 0; i <= 60; i++) {
            if (l & (1ll << i)) mxl = i;
        }
//        cout << mxl<<' ';
        int ck = (1ll << (mxl + 1));
        if (ck <= r) {
            if (k == n || k == n - 1) cout << ck << '\n';
            else cout << l << '\n';
        } else {
            cout << -1 << '\n';
        }
    }
}

D.Token Removing

有一个数组{\(a_i\)},这个数组上的每一个数字满足\(0\leqslant a_i \leqslant i\) ,依次进行\(n\)次操作,第\(i\)次操作若\(a_i \ne 0\) ,则删除[\(a_i,i\)]之间的一个标记,求所有合法数组{\(a_i\)}删除标记的方案数,答案对m取模。

解析:注意这里需要的到的是方案数而不是数组 (本蒟蒻在这里绕了半天) 。由于每一次操作只会影响[\(a_i,i\)]不会影响\(i\)之后的部分,所以我们考虑从后往前dp这样后面的数字不会影响到已经形成的方案。我们定义\(dp[i][j]\)\([i,n]\)中有\(j\)个标记被删除的方案数,递推公式如下:
1.\(i\)处标记未被删除,\(dp[i][j]=dp[i+1][j]\) (那么没有增加新的标记,方案数和上一次一样)
2.\(i\)处标记被删除,\(dp[i][j]=dp[i+1][j-1]*i*(n-i+1-j)\) (增加了一个新的标记,为了增加这个新的标记可以在那些未被删除标记的位置即共\((n-i+1-j)\)个位置填上\([1-i]\)那么就可以将\(i\)处的标记删除即原方案{a,b,c,d}变为{i,a,b,c,d},方案为{i,a,b,c,d}的有\(i*(n-i+1-j)\)种)

void solve() {  
    int n, mod;  
    cin >> n >> mod;  
    vector<vector<int>> dp(n + 2, vector<int>(n + 2, 0));  
    dp[n][0] = 1;  
    for (int i = n; i >= 1; i--) {  
        for (int j = 0; j <= n - i; j++) {  
            dp[i - 1][j] = (dp[i - 1][j] + dp[i][j]) % mod;  
            dp[i - 1][j + 1] = (dp[i - 1][j + 1] + dp[i][j] * i % mod * (n - i - j + 1) % mod) % mod;  
        }  
    }  
    int ans = 0;  
    for (int i = 0; i <= n; i++) {  
        ans=(ans+dp[0][i])%mod;  
    }  
    cout << ans <<'\n';  
}
posted @ 2025-07-06 15:22  yoez123  阅读(69)  评论(0)    收藏  举报