May Cook-Off 2019 解题报告

 

太气了。Atcoder unrated了。

这一场时间太不友好了。昨天下午一时兴起就去补了一发。题很好,学到好多东西。

 

Chain Reaction

题意:给一个矩阵,这个矩阵是稳定的当且仅当每一个元素都是稳定的。元素是稳定的当且仅当它的值严格小于它相邻有几个元素

思路:四个角的值就必须小于2,然后外圈的其他数的值就必须小于3,其他的就必须小于4。

#include <bits/stdc++.h>
using namespace std;

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
    return x * f;
}

const int N = 20;
int a[N][N];

int main() {
    int T = read();
    while (T--) {
        int r = read(), c = read();
        bool ans = false;
        for (int i = 1; i <= r; i++) {
            for (int j = 1; j <= c; j++) {
                a[i][j] = read();
                if (i == 1) {
                    if (i == j || j == c) {
                        if (a[i][j] >= 2) ans = true;
                    } else {
                        if (a[i][j] >= 3) ans = true;
                    }
                } else if (i == r) {
                    if (1 == j || j == c) {
                        if (a[i][j] >= 2) ans = true;
                    } else {
                        if (a[i][j] >= 3) ans = true;
                    }
                } else if (j == 1 || j == c) {
                    if (a[i][j] >= 3) ans = true;
                } else {
                    if (a[i][j] == 4) ans = true;
                }
            }
        }
        if (ans) puts("Unstable");
        else puts("Stable");
    }     
}
View Code

 

Recover the Sequence

题意:一个等差数列,其中一个数被改了,要求原数列。

思路:四项肯定可以出一个等差数列,只看前四项就ok了。

#include <bits/stdc++.h>
using namespace std;

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
    return x * f;
}

const int N = 1e5 + 10;
int a[N], b[N];

int main() {
    int T = read();
    while (T--) {
        int n = read();
        for (int i = 1; i <= n; i++) a[i] = read();
        if (a[2] - a[1] == a[3] - a[2]) {
            int d = a[2] - a[1];
            for (int i = 2; i <= n; i++) {
                a[i] = a[i-1] + d;
            }
        } else if (a[3] - a[2] == a[4] - a[3]) {
            int d = a[3] - a[2];
            for (int i = n - 1; i > 0; i--) {
                a[i] = a[i+1] - d;
            }
        } else {
            int d = (a[4] - a[1]) / 3;
            for (int i = 2; i <= n; i++) {
                a[i] = a[i - 1] + d;
            }
        }
        for (int i = 1; i <= n; i++) { if (i - 1) putchar(' '); printf("%d", a[i]); }
        puts("");
    }
    return 0;
}
View Code

 

Misha and Nice Sets

题意:给一个区间$l,r$和一个数$g$,要求这个区间内最多有几个数的GCD刚好为$g$

思路:容斥求,WA了好几发,原因是。当容斥出来结果只有1个时,$g$必须在$l,r$之间,不然这一个数的最大因子是自己而不是$g$。

题目描述也是The greatest positive integer which divides each element of the set is exactly $g$,一个数的greatest positive integer which divide it是它本身。

#include <bits/stdc++.h>
#define ll long long
using namespace std;

inline ll readl() {
    ll x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
    return x * f;
}

int main() {
    ll T = readl();
    while (T--) {
        ll l = readl(), r = readl(), g = readl();
        ll ans = r / g - l / g + (l % g == 0);
        if (ans == 1) {
            if (g < l || g > r) ans = 0;
        }
        printf("%lld\n", ans);
    }
    return 0;
}
View Code

 

Searching for a Biclique

题意:给一个有向图,问能不能删边删点使他们成为一个二分图 一边点数为2,一边点数为$k$

思路:暴力求。不知道为啥不会T。咱也不知道,咱也不敢问。

#include <bits/stdc++.h>
using namespace std;

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
    return x * f;
}

const int N = 2e3 + 10;
vector<int> G[N];

int main() {
    int n = read(), m = read(), k = read();
    for (int i = 0; i < m; i++) {
        int u = read(), v = read();
        G[u].emplace_back(v);
        G[v].emplace_back(u);
    }
    for (int i = 1; i <= n; i++) sort(G[i].begin(), G[i].end());
    for (int i = 1; i <= n; i++) {
        for (int j = i + 1; j <= n; j++) {
            int p1 = 0, p2 = 0;
            int ans = 0;
            int sz1 = G[i].size(), sz2 = G[j].size();
            while (p1 < sz1 && p2 < sz2) {
                if (G[i][p1] == G[j][p2]) {
                    p1++;p2++; ans++;
                } else if (G[i][p1] < G[j][p2]) {
                    p1++;
                } else p2++;
            }
            if (ans >= k) { puts("YES"); return 0;}
        }
    }
    puts("NO");
    return 0;
}
View Code

看了题解的解法。复杂度就比较科学了。$O\left( n^{2}k\right)$

#include <bits/stdc++.h>
using namespace std;

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
    return x * f;
}

const int N = 2e3 + 10;
vector<int> G[N];
int cnt[N][N];

int main() {
    int n = read(), m = read(), k = read();
    for (int i = 0; i < m; i++) {
        int u = read(), v = read();
        G[u].emplace_back(v);
        G[v].emplace_back(u);
    }
    bool ans = false;
    for (int i = 1; i <= n; i++) {
        if (ans) break;
        for (auto x: G[i]) {
            if (ans) break;
            for (auto y: G[i]) {
                if (x < y) {
                    cnt[x][y]++;
                    cnt[y][x]++;
                }
                if (cnt[x][y] >= k) {
                    ans = true;
                    break;
                }
            }
        }
    }
    if (ans) puts("YES");
    else puts("NO");
    return 0;
}
View Code

 

Expected Xor

题意:给n个物品,每个物品有值$b_{i}$和选中概率$p_{i}$,求异或和的期望

思路:emmmm概率题本来就不太会再加上异或emmmmm

按位考虑每一个数,因为异或是两个相同就变成0了。那么能对期望做出贡献的时候就是二选一的时候

pro表示在当前位,当前数能对期望做出贡献的概率。

到下一个数的时候就是$pro\ast \left( 1-p_{i}\right) + p_{i}\ast \left( 1-pro\right)$

最后乘上(1 << k) 就行了 k表示当前位

#include <bits/stdc++.h>
using namespace std;

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
    return x * f;
}

const int N = 1e5 + 10;
int b[N], n;
double p[N];

int main() {
    int T; scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        for (int i = 0; i < n; i++) scanf("%d", &b[i]);
        for (int i = 0; i < n; i++) scanf("%lf", &p[i]);
        long double ans = 0;
        for (int bit = 0; bit < 32; bit++) {
            long double pro = 0;
            for (int i = 0; i < n; i++) {
                if ((b[i] >> bit) & 1) {
                    pro = (1 - pro) * (long double)p[i] + pro * (1 - (long double)p[i]);
                }
            }            
            ans += (1 << bit) * pro;
        }
        printf("%.7Lf\n", ans);    
    }  
    return 0;
}
View Code

 

Bitsetbaba and Power Grid

题意:有节点0到$2^{k} - 1$,给一个序列$m_{i}$ ,所有点对$u,v$ $u\oplus v = m_{i}$的之间连边,问最后有多少个连通块

思路:推了半天结论也没推明白。看题解才明白是高斯消元。

对于一个节点$u$,它能到达的点是这个序列的数,任意组合,异或之后的值的个数(加上一个0)

本来是只能异或这个序列里面的一个数,但是由于别的数也会异或,就会连通了。

比如两个节点1 和 2

$m_{1} = 1$ $m_{2} = 2$

1和$m_{1}$异或后是0 和$m_{2}$异或后是3 2和$m_{1}$异或后是3

本来只异或一个值的话,1和2看起来是不连通的

但就是会间接相连 所以就是xjb组合着去异或

然后答案就是看这个序列能异或出多少种值来。

把它们按二进制排成一个矩阵。

求出这个矩阵的秩$r$

答案就是$2^{k - r}$

解释:把矩阵通过异或运算变成行阶梯型之后,秩为$r$,前$r$行的线性组合得到的结果必是不一样了

然后取和不取种类数就是$2^{r}$种

#include <bits/stdc++.h>
using namespace std;

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
    return x * f;
}

const int N = 1e5 + 10;
int a[N];
int bin[N];

int main() {
    int T = read();
    while (T--) {
        memset(bin, 0, sizeof(bin)); 
        int k = read(), m = read();
        for (int i = 0; i < m; i++) {
            int x = read();
            for (int j = 0; j < 31; j++) {
                if ((1<<j) & x) {
                    if (!bin[j]) {
                        bin[j] = x;
                        k--;
                        break;
                    }
                    x ^= bin[j];
                }
            }
        }
        cout << (1 << k) << '\n';
    }    
    return 0;
}
View Code

 

Nearest Color

题意:一棵树,刚开始只有根1和颜色$c$,给$Q$次询问,询问有两种,第一种是给节点$u$加上一个儿子(编号为当前节点数+1),颜色染成$c$,第二种询问颜色为$c$的节点中,和$u$最近的距离是多少。

思路:直接写了发暴力找+倍增LCA,T了。看了题解才发现有trick。

把颜色分为大和小。以300次为界限。

出现次数小于300的就可以直接暴力+LCA

大于300的把颜色为$c$的节点$u$ $dis[c][u] = 0$并且对这些节点进行bfs

bfs过程中若dis为INF的话就可以更新成$dis[c][u] + 1$

#include <bits/stdc++.h>
using namespace std;

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
    return x * f;
}

const int N = 2e5 + 10;
const int RT = 300;
const int INF = 0x3f3f3f3f;
vector<int> group[N];
vector<int> rem[N];
vector<int> big;
vector<int> adj[N];
vector<int> dis[N];
int fa[N], lca[N][25], n, dep[N], q, col[N];

void recalc(int c) {
    if (dis[c].empty()) big.emplace_back(c);
    dis[c].assign(n + 1, N);
    queue<int> que;
    for (int u: group[c]) {
        dis[c][u] = 0;
        que.push(u);
    } 
    while (!que.empty()) {
        int u = que.front(); que.pop();
        for (int v: adj[u]) {
            if (dis[c][v] == N) {
                dis[c][v] = dis[c][u] + 1;
                que.push(v);
            }
        }
    }
    rem[c].clear();
}

void add(int u, int c) {
    n++;
    col[n] = c;
    lca[n][0] = fa[n] = u;
    adj[n].emplace_back(u);
    adj[u].emplace_back(n);
    dep[n] = dep[u] + 1;
    for (int i = 1; i <= 20; i++) lca[n][i] = lca[lca[n][i-1]][i-1];
    for (int cc: big) dis[cc].emplace_back(dis[cc][u] + 1);
    group[c].emplace_back(n);
    rem[c].emplace_back(n);
    if (rem[c].size() >= RT) recalc(c);   
}

int Lca(int u, int v) {
    if (dep[u] < dep[v]) swap(u, v);
    int f = dep[u] - dep[v];
    for (int i = 0; i <= 20; i++) {
        if (f & (1 << i)) {
            u = lca[u][i];
        }
    }
    if (u == v) return v;
    for (int i = 20; i >= 0; i--) {
        if (lca[u][i] != lca[v][i]) {
            u = lca[u][i];
            v = lca[v][i];
        }
    }
    return lca[u][0];
}

int distance(int u, int v) {
    return dep[u] + dep[v] - 2 * dep[Lca(u, v)];
}

int query(int u, int c) {
    int ans = dis[c].size() ? dis[c][u] : N;
    for (int v: rem[c]) ans = min(ans, distance(u, v));
    if (ans == N) ans = -1;
    return ans;
}

void init() {
    for (int i = 0; i < N; i++) {
        adj[i].clear();
        group[i].clear();
        dis[i].clear();
        rem[i].clear();
    }
    big.clear();
    n = 1;
}

int main() {
    int T = read();
    while (T--) {
        init();
        q = read();int c = read();
        group[c].emplace_back(1);
        rem[c].emplace_back(1);
        dep[1] = 0; fa[1] = 1;
        int a = -1;
        while (q--) {
            char s[10];
            scanf("%s", s);
            int u = read(); c = read();
            u = u ^ (a + 1); c = c ^ (a + 1);
            if (s[0] == '+') add(u, c);
            else printf("%d\n", a = query(u, c));
        }
    }
    return 0; 
}
View Code

 

还是太菜了呀...

posted @ 2019-05-21 16:26  Mrzdtz220  阅读(181)  评论(0)    收藏  举报