第十五届吉林省大学生程序设计竞赛个人题解

A. Random Number Checker

奇数和偶数的数量之差小于1即为Good

B. Arithmetic Exercise

计算保留k位小数的a/b的结果

模拟除法即可,注意进位的细节

void solve() {
    ll a, b , k;
    cin >> a >> b >> k;
    vector <int> ve(1, a/b);
    ll c = a % b;
    for (int i = 1; i <= k+1; i++) {
        c *= 10;
        ve.push_back(c / b);
        c %= b;
    }
    if (ve[k+1] >= 5) {
        ve[k]++;
    }
    for (int i = k; i > 0; i--) {
        if (ve[k] >= 10) {
            ve[k] %= 10;
            ve[k-1]++;
        } else break;
    }
    cout << ve[0] << '.';
    for (int i = 1; i <= k; i++) {
        cout << ve[i];
    }
}

E. Great Detective TJC

找出给定的n个数中是否有一对异或值为1的两个数

方法一:排序后依次check相邻两数的异或值,时间复杂度O(nlogn)

方法二:把检查set里是否有当前数^1,然后把它插入set里,时间复杂度O(nlogn)

        

void solve() {
    cin >> n;
    set<int> st;
    mark = 0;
    for (int i = 0; i < n; i++) {
        cin >> b;
        if (st.count(b^1)) mark = 1;
        st.insert(b);
    }
    if (mark) cout << "Yes\n";
    else cout << "No\n";
}

G. Matrix Repair

 给出一个部分残缺的01矩阵以及完好的该矩阵的每行每列的异或值,输出完整矩阵或-1

把行表示为0到n-1,列表示为n到2*n-1

给出的每行每列的异或值叫做res[i],每行每列已知的数的亦或和叫做xor[i]

易知当某一行 / 列仅剩一个未知数时可以用res[i] ^ xor[i]得到这个未知数的值

这个未知数解出来后可以继续去为它所在的那一列 / 行做贡献

可以用拓扑排序来考虑

即把每行 / 列作为一个点,未知数作为一条边连接它所在的行和列

度为1时可以求出这个未知数的值

如果拓扑排序结束后仍有边则说明有环无解

vector <vector <int> > ve, tu;
vector <int> d, xo, res;

void change(int a, int b) {
    int c = xo[a] ^ res[a];
    xo[b] ^= c;
    if (a > b) swap(a, b);
    b -= n;
    tu[a][b] = c;
}

void solve() {
    cin >> n;
    queue<int> q;
    ve = vector<vector<int> > (n<<1);
    tu = vector<vector<int> > (n, vector<int> (n));
    res = xo = d = vector<int> (n<<1);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> tu[i][j];
            if (tu[i][j] == -1) {
                d[i]++;
                d[n+j]++;
                ve[i].push_back(n+j);
                ve[n+j].push_back(i);
            } else {
                xo[i] ^= tu[i][j];
                xo[n+j] ^= tu[i][j];
            }
        }
    }
    for (int i = 0; i < n*2; i++) cin >> res[i];
    for (int i = 0; i < n*2; i++) {
        if (d[i]) {
            if (d[i] == 1) {
                q.push(i);
            }
        }
    }

    while (q.size()) {
        int now = q.front();
        q.pop();
        if (!d[now]) continue;
        d[now] = 0;
        for (int i : ve[now]) {
            if (d[i]) {
                d[i]--;
                change(now, i);
                if (d[i] == 1) q.push(i);
            }
        }
    }

    for (int i = 0; i < n*2; i++) {
        if (d[i]) {
            cout << "-1\n";
            return;
        }
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cout << tu[i][j] << (j == n-1 ? '\n' : ' ');
        }
    }
}

 H. Visit the Park

给出一个图,输出按要求的节点顺序行走的权值的期望

注意两点之间可能会有多条边,存图需要特殊处理,笔者使用的是map<ll, vector<int> >

第一维存起点与终点的hash值(u<<30 | v),第二位存u->v的权值集合

然后按照给出的点的顺序遍历对应的vector即可

#define mk(a, b) (a<<30 | b)
unordered_map<ll, vector<int> > mp;
const int mod = 998244853;
int Power( ll x , int y ) {
    ll res = 1;
    while(y) {
        if (y & 1) (res *= x) %= mod;
        x = x * x % mod;
        y >>= 1;
    }
    return res;
}

ll ny (int now) {
    return Power(now, mod-2);
}

void solve() {
    cin >> n >> m >> k;
    for (int i = 0; i < m; i++) {
        cin >> a >> b >> c;
        mp[mk(a, b)].push_back(c);
        mp[mk(b, a)].push_back(c);
    }
    int now, nxt;
    cin >> now;
    ll ans = 0;
    ll P10 = Power(10, k-2);
    const ll ny10 = ny(10);
    for (int i = 1; i < k; i++) {
        cin >> nxt;
        const ll NOW = mk(now, nxt);
        if (!mp.count(NOW)) {
            cout << "Stupid Msacywy!";
            return;
        }
        int nynow = ny(mp[NOW].size());
        for (int j : mp[NOW]) {
            (ans += j * P10 % mod * nynow % mod) %= mod;
        }
        P10 = P10 * ny10 % mod;
        now = nxt;
    }
    cout << ans << '\n';
}

 I. Nim Game

对1e5数组询问 / 修改1e5次,修改是[l, r]加x,

询问是[l, r]的集合是否有一个可以使得nim博弈后手胜利(即异或和为0)的子集

修改可使用树状数组 / 线段树,单次操作时间复杂度为Olog(n)

由题知每个数的上界是2e9,由线性基可知不合法区间的最大长度为\left \lfloor log(2e9) \right \rfloor + 1 = 31

所以若区间长度大于31就可以直接确定,否则可利用线性基求解,

单次操作时间复杂度为Olog(2e9)^{2}

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#ifdef LOCAL
template<class t,class u>ostream& operator<<(ostream& os,const pair<t,u>& p){return os<<"("<<p.first<<","<<p.second<<")";}
template<class t>ostream& operator<<(ostream& os,const vector<t>& v){os<<"{"; for(auto e:v)os<<e<<","; return os<<"}";}
#endif
int n, m, t, k, a, b, c, d, cnt, mark, an, oT_To, x, y, z, ans;

ll tree[100005];

int lowbit(int x) {
    return x & (-x);
}

void update(int x, int val) {
    for (; x < n; x += lowbit(x)) {
        tree[x] += val;
    }
}

ll ask(int x) {
    ll res = 0;
    for (; x; x -= lowbit(x)) {
        res += tree[x];
    }
    return res;
}

vector <int> xx;
bool insert(int x) {
    for (int i : xx) {
        x = min(x, i ^ x);
    }
    if (!x) return 0;

    for (int &i : xx) {
        i = min(i, i ^ x);
    }
    xx.push_back(x);
    return 1;

}


int main () {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    vector <int> ve (n+1);
    for (int i = 1; i <= n; ++i) {
        cin >> ve[i];
    }
    for (int i = 0; i < m; ++i) {
        int op, l, r;
        cin >> op >> l >> r;
        switch (op) {
            case 1:
                cin >> x;
                update(l, x), update(r + 1, -x);
                break;
            case 2:
                if (r - l + 1 > 30) {
                    cout << "Yes\n";
                } else {
                    xx.clear();
                    int i = l;
                    while (i <= r && insert(ve[i] + ask(i)) ) i++;
                    if (i > r) {
                        cout << "No\n";
                    } else cout << "Yes\n";
                }
        }
    }

}

K. Bracket Sequence

求出n对k种合法的括号匹配序列的数量

易知n,1时结果为卡特兰数的第n项

每对括号都有k种可能性

故结果为 卡特兰数第n项* k^{n}%mod

int main() {
    cin >> n >> k;
    cout << C(n*2, n) * ny(n+1) % mod * Power(k, n) % mod << '\n';
}

L. Suzuran Loves String

 找出两个最长的前缀不同的后缀匹配

易知答案最小是len-1,此时字符串形如aaaaaa

找出第一个与str[0]不同的字符的下标i即可,两个后缀就是str和str+i

void solve() {
    string s;
    cin >> s;
    ll ans = s.size() - 1;
    for (int i = 1; i < s.size(); i++) {
        if (s[i] != s[0]) {
            ans = s.size() * 2 - i;
            break;
        }
    }
    cout << ans << '\n';
}

M. Sequence

输出(max - min)*len

void solve() {
    cin >> n;
    vector <int> ve(n);
    for (int i = 0; i < n; i++) {
        cin >> ve[i];
    }
    cout << (ll)n * (*max_element(ve.begin(), ve.end()) - *min_element(ve.begin(), ve.end()));
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值