Manthan, Codefest 18 (rated, Div. 1 + Div. 2)

A - Packets

题意:给n个硬币,要求把这些硬币装在尽可能少的袋子里,并能表示出[1,n]的所有数,求袋子数。

题解:以前没想过为什么这个东西的本质是二进制,其实“用”和“不用”就是“1”和“0”,那么要表示[1,n]的所有数肯定需要各个二进制位都至少有1个,然后剩下假如有零头就零头自己装一个。

void test_case() {
    int n;
    scanf("%d", &n);
    int x = 1;
    while((1 << x) <= n)
        ++x;
    printf("%d\n", x);
}

B - Reach Median

题意:给n(n是奇数)个数,每次操作给一个数+1或者-1,求最少的操作次数使得中位数恰好为s。

题解:排序。然后判断s是不是恰好在中位数,是则0,否则:若中位数大于s,则需要把这个数之前的比s大的数全部改成s;否则要把这个数之后的比s小的数全部改成s。可以出一个强化版。

int a[200005];

void test_case() {
    int n, s;
    scanf("%d%d", &n, &s);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    sort(a + 1, a + 1 + n);
    int mid = (n + 1) / 2;
    if(a[mid] == s) {
        printf("0\n");
        return;
    } else if(a[mid] > s) {
        ll sum = 0;
        for(int i = 1; i <= mid; ++i) {
            if(a[i] > s)
                sum += a[i] - s;
        }
        printf("%lld\n", sum);
        return;
    } else {
        ll sum = 0;
        for(int i = mid; i <= n; ++i) {
            if(a[i] < s)
                sum += s - a[i];
        }
        printf("%lld\n", sum);
        return;
    }
}

C. Equalize

题意:给定n长度的01字符串s和t,对s进行尽可能少的操作使得它变为t。一次操作的cost为:直接修改cost=1,交换i,j位置的数cost=|i-j|。

题解:除了临位交换,其他的直接改。

int n;
char s[2000005];
char t[2000005];

void test_case() {
    scanf("%d%s%s", &n, s + 1, t + 1);
    int sum = 0;
    for(int i = 1; i <= n - 1; ++i) {
        if(s[i] != s[i + 1] && s[i] != t[i] && s[i + 1] != t[i + 1]) {
            ++sum;
            swap(s[i], s[i + 1]);
        } else if(s[i] != t[i]) {
            ++sum;
            s[i] = t[i];
        }
    }
    if(s[n] != t[n]) {
        ++sum;
        s[n] = t[n];
    }
    printf("%d\n", sum);
}

D. Valid BFS?

题意:给一棵以1为根的树,验证一个序列是不是(其中一种合法的)BFS序。

题解:按照题目给的序列标记同一个节点各儿子的先后顺序,然后得到的BFS序必定是唯一的,再全文比较。

int n;
vector<int> G1[200005];
int pos[200005];

bool vis[200005];

set<pii> G2[200005];
queue<int> Q;

int ans[200005];

void bfs() {
    Q.push(1);
    vis[1] = 1;
    int cnt = 0;
    while(!Q.empty()) {
        int u = Q.front();
        ans[++cnt] = u;
        Q.pop();
        for(auto &i : G2[u]) {
            int v = i.second;
            if(vis[v])
                continue;
            Q.push(v);
            vis[v] = 1;
        }
    }
    if(cnt != n) {
        puts("No");
        return;
    }
    for(int i = 1; i <= n; ++i) {
        if(ans[pos[i]] != i) {
            puts("No");
            return;
        }
    }
    puts("Yes");
}

void test_case() {
    scanf("%d", &n);
    for(int i = 1; i <= n - 1; ++i) {
        int u, v;
        scanf("%d%d", &u, &v);
        G1[u].push_back(v);
        G1[v].push_back(u);
    }
    for(int i = 1; i <= n; ++i) {
        int x;
        scanf("%d", &x);
        pos[x] = i;
    }
    for(int i = 1; i <= n; ++i) {
        for(auto v : G1[i])
            G2[i].insert({pos[v], v});
    }
    bfs();
}

E. Trips

题意:给一个n个点m条边的无向图,每条边是依次加入的,求每次加入边后,当前的图中后能选出的最多的点,使得他们的度数都不小于k。

题解:不知道正着能不能做,反着一定可以做,先把整个图构造出来,标记所有度数<k的点,循环删除直到剩下的点都满足度数不小于k,然后依次删除边,注意若一条边的其中一端被删除过那么就不要重复删除了。注意处理好删除点和删除边的联系就可以。

但是真正写的时候发现联系还挺复杂的,容易把边重复删除,用set就没这个问题了。

int n, m, k;
int U[200005], V[200005];
int d[200005];
set<int> G[200005];

bool vis[200005];
int ans[200005];

int cnt;
void DeleteV(int u) {
    if(vis[u])
        return;
    vis[u] = 1;
    /*printf("delete u=%d\n", u);
    for(int j = 1; j <= n; ++j)
        printf(" %d", d[j]);
    printf("\n");*/
    ++cnt;
    for(auto &v : G[u]) {
        if(vis[v])
            continue;
        --d[u];
        --d[v];
        G[v].erase(u);
        if(d[v] < k)
            DeleteV(v);
    }
    G[u].clear();
}

void test_case() {
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= m; ++i) {
        int u, v;
        scanf("%d%d", &u, &v);
        U[i] = u, V[i] = v;
        G[u].insert(v);
        G[v].insert(u);
        ++d[u];
        ++d[v];
    }
    for(int i = 1; i <= n; ++i) {
        if(!vis[i] && d[i] < k)
            DeleteV(i);
    }
    for(int i = m; i >= 1; --i) {
        /*printf("i=%d\n", i);
        for(int j = 1; j <= n; ++j)
            printf(" %d", d[j]);
        printf("\n");*/
        ans[i] = n - cnt;
        int u = U[i], v = V[i];
        if(vis[u] || vis[v])
            continue;
        --d[u];
        --d[v];
        G[u].erase(v);
        G[v].erase(u);
        if(!vis[u] && d[u] < k)
            DeleteV(u);
        if(!vis[v] && d[v] < k)
            DeleteV(v);
    }
    for(int i = 1; i <= m; ++i)
        printf("%d\n", ans[i]);
}
posted @ 2020-01-29 00:16  KisekiPurin2019  阅读(138)  评论(0编辑  收藏  举报