whp 6 - Codeforces Round #767 (Div. 2)

Codeforces Round #767 (Div. 2)

Codeforces Round #767 (Div. 2)

A. Download More RAM

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const ll N = 110;

struct node {
    ll a, b;
};

node mp[N];

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    ll T;
    cin >> T;
    while (T--) {
        ll n, k;
        cin >> n >> k;
        for (ll i = 1; i <= n; ++i) {
            cin >> mp[i].a;
        }
        for (ll i = 1; i <= n; ++i) {
            cin >> mp[i].b;
        }
        sort(mp + 1, mp + n + 1, [&](node x, node y) {
            return x.a < y.a;
        });
        for (ll i = 1; i <= n; ++i) {
            if (mp[i].a <= k) {
                k += mp[i].b;
            }
        }
        cout << k << endl;
    }
    return 0;
}

B. GCD Arrays

题意:给出l和r表示有从l到r这些数,一次操作可以选择两个数取出然后将他们的乘积放回去,问能否在k次操作内使得剩余数的gcd大于1

使得gcd包含2将所有的奇数和任意一个偶数合并的操作数为奇数的个数

判断k是否大于等于奇数的个数

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;


signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    ll T;
    cin >> T;
    while (T--) {
        int l, r, k;
        cin >> l >> r >> k;
        int num = (r - l + 1)/ 2;
        if((l & 1) && (r & 1)) num ++;
        if(k >= num)  {
            cout << "YES" << endl;
            continue;
        }
        if(r == l && r > 1) {
            cout << "YES" << endl;
            continue;
        }
        cout <<"NO" << endl;

    }
    return 0;
}

C. Meximum Array

题意:给一个数组a,每次可以从头选择若干连续的数字从a中删去,将他们的mex值加入到b的末尾,要求使得b数组尽可能的大,输出b数组

统计所有数字出现的次数和位置(这里使用了队列因为我觉得删的方便

对于每个mex将数字出现的位置维护

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 2e5 + 10;
int a[N];
queue<int> g[N];
vector<int> res;

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    ll T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        res.clear();
        for (int i = 1; i <= n; ++i) {
            cin >> a[i];
            g[a[i]].push(i);
        }
        int pos = 0;
        while(pos < n) {
            for (int i = 0; i <= n; ++i) {
                if (g[i].empty()) {
                    res.push_back(i);
                    break;
                }
                pos = max(pos, g[i].front());
            }
            if(res[res.size() - 1] == 0) {
                pos ++;
                continue;
            }
            for(int i=0; i < res[res.size() - 1]; ++ i) {
                while(g[i].size() && g[i].front() <= pos) g[i].pop();
            }
        }

        cout << res.size() << endl;
        for(auto tmp : res) cout << tmp <<" ";
        cout << endl;

        //init
        for (int i = 0; i <= n; ++i) {
            while(!g[i].empty()) g[i].pop();
        }
    }
    return 0;
}

D. Peculiar Movie Preferences

题意:有n个字符串(长度不超过3),要求保留至少一个,使得他们连接起来形成回文串

如果一个串本身就是回文,直接留它就行,除去该情况之后

不然只要是两个拼起来是回文就行,即

(AB)(BA)

(ABC)(BA)

(AB)(CBA)

(ABC)(CBA)

对这些情况直接进行判断:对于BA和CBA可以构造出AB和ABC等,用set维护,剩下的一个ABC在前BA在后的额外倒过来扫一次

(至于为什么不用判别的不会证明但好像就是不用判,
证明可以看下官方题解

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

set<string> st;
string a[N];

inline bool check(string s) {
    if (s.size() == 1) return 1;
    if (s.size() == 2)
        return (s[0] == s[1]);
    if (s.size() == 3)
        return (s[0] == s[2]);
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        st.clear();
        bool flag = 0;
        for (int i = 1; i <= n; ++i) {
            string s;
            cin >> s;
            a[i] = s;
            if (check(s)) flag = 1;
            if (s.size() == 2) {
                string t = "";
                t = t + s[1] ;
                t = t + s[0];
                if (st.count(t)) flag = 1;
            }
            if (s.size() == 3) {
                string t = "";
                t = t + s[2];
                t = t + s[1];
                if (st.count(t)) flag = 1;
                t = t + s[0];
                if (st.count(t)) flag = 1;
            }
            st.insert(s);
        }
        if (flag) {
            cout << "YES" << endl;
            continue;
        }

        st.clear();
        for (int i = n; i >= 1; --i) {
            string s = a[i];
            if (s.size() == 3) {
                string t = "";
                t = t+ s[1];
                t = t + s[0];
                if (st.count(t)) {
                    flag = 1;
                    break;
                }
            }
            st.insert(s);
        }
        if (flag) {
            cout << "YES" << endl;
        } else {
            cout << "NO" << endl;
        }
    }
}

E. Grid Xor

题意: n × n n \times n n×n 的格子里每个格子有一个数,现在告诉每个格子周围格子的异或和,问整个格子的总异或和是多少

直接转换成翻格子游戏(黑变白-白变黑那个)

相邻两个格子可以凑成六边形,然后六边形可以无缝拼接,只要再处理一下边角就行

在这里插入图片描述

然后第一排搞好后面的根据第一排铺就可以了

照常谢谢hoshimi同学,明天补代码,眠了

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const ll N = 1010;
ll a[N][N], b[N][N];
ll n;


inline void init() {
    for (ll i = 0; i <= n; ++i) {
        for (ll j = 0; j <= n; ++j) {
            b[i][j] = 0;
            a[i][j] = 0;
        }
    }
}

void sign(ll x, ll y) {
    if (x - 1 >= 1 && x - 1 <= n) b[x - 1][y] = 1 - b[x - 1][y];
    if (x + 1 >= 1 && x + 1 <= n) b[x + 1][y] = 1 - b[x + 1][y];
    if (y - 1 >= 1 && y - 1 <= n) b[x][y - 1] = 1 - b[x][y - 1];
    if (y + 1 >= 1 && y + 1 <= n) b[x][y + 1] = 1 - b[x][y + 1];
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    ll T;
    cin >> T;
    while (T--) {
        cin >> n;
        for (ll i = 1; i <= n; ++i) {
            for (ll j = 1; j <= n; ++j) {
                cin >> a[i][j];
            }
        }
        ll ans = 0;
        for (ll i = 1; i <= n; i += 4) {
            ans = ans ^ a[1][i];
            ans = ans ^ a[1][i + 1];
            sign(1, i), sign(1, i + 1);
        }
        for (ll i = 2; i <= n; ++i) {
            for (ll j = 1; j <= n; ++j) {
                if (b[i - 1][j] == 0) {
                    ans = ans ^ a[i][j];
                    sign(i, j);
                }
            }
        }
        cout << ans << endl;

        init();
    }
}

F1. Game on Sum (Easy Version)

题意:Alice和Bob在玩游戏,A先选一个在 [ 0 , k ] [0, k] [0,k]的实数,然后B决定从当前的结果中加上它还是减去它,一共有n轮,B在这n轮中至少要加m轮(B每次知道A当前的选择数是多少,A每次知道之前的结果),A要使得结果最大,B使得最小

考虑dp

d p [ i ] [ j ] : = dp[i][j]:= dp[i][j]:= 一共i轮 ,其中至少j轮是加的结果

然后有一些边界条件:

d p [ i ] [ 0 ] = 0 dp[i][0] = 0 dp[i][0]=0; //i轮全减的情况

d p [ i ] [ i ] = i × k dp[i][i] = i \times k dp[i][i]=i×k ; //i轮全加的情况

假设当前A选择的数是x,B要使得结果尽可能小, 则 D P [ i ] [ j ] = m i n ( D P [ i − 1 ] [ j − 1 ] + x , D P [ i − 1 ] [ j ] − x ) DP[i][j]=min(DP[i−1][j−1]+x,DP[i−1][j]−x) DP[i][j]=min(DP[i1][j1]+x,DP[i1][j]x)

注意到两边都是线性的,所以可以直接除以2, (这里可以用小数据来想一下,比如n = 2, m = 1的数据

于是得到最后的转移式子为

D P [ i ] [ j ] = ( D P [ i − 1 ] [ j − 1 ] + D P [ i − 1 ] [ j ] ) / 2 DP[i][j]=(DP[i−1][j−1]+DP[i−1][j])/2 DP[i][j]=(DP[i1][j1]+DP[i1][j])/2

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const ll N = 2010;
const ll mod = 1e9 + 7;
ll dp[N][N];

ll n, m, k;

ll qpow(ll x, ll n) {
    ll res = 1;
    while (n) {
        if (n & 1) res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

void solve() {
    for (ll i = 1; i <= n; ++i) {
        dp[i][0] = 0;
        dp[i][i] = i * k % mod;
    }
    for (ll i = 2; i <= n; ++i) {
        for (ll j = 1; j < i; ++j) {
            dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % mod * qpow(2, mod - 2) % mod;
        }
    }
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);


    ll T;
    cin >> T;
    while (T--) {
        cin >> n >> m >> k;
        solve();
        cout << dp[n][m] << endl;
    }
    return 0;
}

F2

在F1的基础上扩大了数据范围
需要优化dp的转移
观察转移可以发现dp初始值来源是 d p [ i ] [ i ] dp[i][i] dp[i][i]
每次转移都除以2
则考虑这样一个转移图
在这里插入图片描述
每个 d p [ n ] [ m ] dp[n][m] dp[n][m]的值的来源其实就是各个 d p [ i ] [ i ] dp[i][i] dp[i][i] , 每个 d p [ i ] [ i ] dp[i][i] dp[i][i] 到 n,m 走有两种移动,一种是直接往下,一种是往下1往右1, 然后每次除以2
就是从若干次往下的转移中挑一些往右
变成一个组合数了就

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const ll N = 1e6 + 10;
const ll mod = 1e9 + 7;

ll n, m, k;

ll qpow(ll x, ll n) {
    ll res = 1;
    while (n) {
        if (n & 1) res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

ll fac[N];// n!
ll invfac[N]; // n!的inv
ll invn[N]; //n的inv

inline void init() {
    fac[0] = fac[1] = invfac[0] = invfac[1] = invn[0] = invn[1] = 1;
    for (ll i = 2; i < N; ++i) {
        fac[i] = fac[i - 1] * i % mod;
        invn[i] = (mod - mod / i) * invn[mod % i] % mod;
        invfac[i] = invfac[i - 1] * invn[i] % mod;
    }
}

ll C(ll down, ll up) {
    if (up > down) return 0;
    if (up < 0 || down < 0) return 0;
    ll res = fac[down];
    res = res * invfac[down - up] % mod;
    res = res * invfac[up] % mod;
    return res;
}


signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);

    init();

    ll T;
    cin >> T;
    while (T--) {
        cin >> n >> m >> k;
        if (m == n) {
            cout << n * k % mod << endl;
            continue;
        }
        if (m == 0) {
            cout << 0 << endl;
            continue;
        }
        ll ans = 0;
        for (ll i = 1; i <= n; ++i) {
            if (i > m) break;
            ans = (ans + i * k % mod * C(n - i - 1, m - i) % mod
                         * qpow(qpow(2, n - i), mod - 2) % mod) % mod;
        }
        cout << ans << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值