CodeForces Gym 101611| Moscow Subregional of Northern Eurasia Programming Contest, ICPC 2017-2018

A: Advertising Strategy

题目大意

题解

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
int main() {
    ll n, k;
    scanf("%lld%lld", &n, &k);

    ll ans = 154164214511LL;
    if (n <= 2) ans = 1;
    for (ll t = 1; t < k; ++t) {
        ll day = 1, x = t, r = k - t;
        while (x + r < n) {
            x += min(x, (n - x) / 2);
            ++day;
        }
        ans = min(ans, day);
    }
    printf("%lld", ans);
    return 0;
}

C: Carpet

题目大意

现在有 1 , 000 , 000 × 20 的网格,求一种方案使得至多 100 , 000 个节点的树放在上面每个节点占一格而且边不相交。

题解

注意到网格长度要大于节点数,那么我们甚至可以每列只放一个节点(虽然实际是不需要的)。然后宽度比较小,我们要处理这个问题。如果我们进行了树链剖分,那么根到任意节点的轻链数是不超过 O ( log n ) 的,而 log 2 n = 16 < 20 ,因此是可以放下的。横着铺重链竖着铺轻链即可。这样每过一次轻链y坐标就+1。
比如如果1有几个子节点,其中一个是重儿子,与1在同一行,剩下的都会在第2行,而其他的在2行的儿子如果有重儿子,那么也都在2行,所以我们可以这么做:将重链堆在同一行,也就是直接每行往右边垒即可。

注意,选择最长路而不是重链是不对的。很容易构造出这样的数据:

1-2-3-4-5-6-7-8
 \
  9-10-11-12-13-14
   \
    15-16-17-18
     \
      19-20

如果点数增加到500层数就会超过20层。训练的时候2支队伍这么做水过去了。。

#include<cstdio>
const int N = 100005, M = N * 2;
int h[N], p[M], v[M], edge, son[N], sz[N], ansx[N], ansy[N], tot[21];

void add(int a, int b) {
    p[++edge] = h[a]; v[edge] = b; h[a] = edge;
}

void dfs(int t, int fa) {
    sz[t] = 1;
    for (int i = h[t]; i; i = p[i])
        if (v[i] != fa) {
            dfs(v[i], t);
            sz[t] += sz[v[i]];
            if (sz[v[i]] > sz[son[t]])
                son[t] = v[i];
        }
}

void dfs2(int t, int fa, int y) {
    ansx[t] = ++tot[y];
    ansy[t] = y;

    for (int i = h[t]; i; i = p[i])
        if (v[i] != fa && v[i] != son[t])
            dfs2(v[i], t, y + 1);

    if (son[t]) dfs2(son[t], t, y);
}

int main() {
    int n, i, x, y;
    scanf("%d", &n);
    for (i = 1; i < n; ++i) {
        scanf("%d%d", &x, &y);
        add(x, y);
        add(y, x);
    }
    dfs(1, 0);
    dfs2(1, 0, 1);
    for (i = 1; i <= n; ++i)
        printf("%d %d\n", ansx[i], ansy[i]);

    return 0;
}

D: Decoding of Varints

题解

大水题。

#include <cstdio>
const int N = 10005;
typedef unsigned long long ull;

int a[N];

int main() {
    int n, i;
    scanf("%d", &n);

    for (i = 1; i <= n; ++i)
        scanf("%d", &a[i]);

    for (i = 1; i <= n; ++i) {
        ull num = 0, base = 1;
        while (i <= n && a[i] >= 128) {
            num += (a[i] - 128) * base;
            base *= 128;
            ++i;
        }
        num += a[i] * base;
        if (num & 1)
            printf("%lld\n", -((num - 1) / 2) - 1);
        else
            printf("%lld\n", num / 2);
    }
    return 0;
}

F: Fake or Leak?

题目大意

首先是ACM赛制,告诉你封榜前的排名,问比赛结束后的排名是否合法。

题解

因为给定的是部分连续的终榜。对于不在终榜里的队伍,只有一题都没过和AK 2种情况需要考虑。如果一题都没过,那么只可能比部分终榜中最后的队伍差,如果AK了,那么只可能比部分终榜中最强的队伍强。否则将破坏连续性。

#include <cstdio>
#include <map>
#include <algorithm>
using namespace std;
const int N = 1005, P = 27;
int n;
struct Team {
    int solved, penalty, lastSubmission;
    string name;
    char state[P];
    int submission[P], time[P];

    Team() {
        solved = penalty = lastSubmission = 0;
    }

    bool operator< (const Team &b) const {
        if (solved != b.solved) return solved > b.solved;
        if (penalty != b.penalty) return penalty < b.penalty;
        if (lastSubmission != b.lastSubmission) return lastSubmission < b.lastSubmission;
        return name < b.name;
    }

    void submit(int i, char type, int sub, int t) {
        state[i] = type;
        submission[i] = sub;
        time[i] = t;
        if (state[i] == '+') {
            ++solved;
            penalty += t + (sub - 1) * 20;  
            lastSubmission = max(lastSubmission, t);    
        }
    }
    void killAll() {
        for (int i = 0; i < n; ++i) {
            if (state[i] == '+') continue;
            if (state[i] == '.')
                submit(i, '+', 1, 240);
            else
                submit(i, '+', submission[i] + 1, 240);

        }
    }

} a[N];
map<string, int> mp;
int final[N], infinal[N];
int main() {
    int sub, t, m, k, i, j, f;
    char s[20];
    scanf("%d%d%d", &n, &m, &k);
    for (i = 0; i < m; i++) {
        scanf("%s", s);
        a[i].name = s;
        mp[a[i].name] = i;
        for (j = 0; j < n; j++) {
            scanf("%s%d%d", s, &sub, &t);
            a[i].submit(j, s[0], sub, t);
        }
    }
    for (f = 0; f < k; f++) {
        scanf("%s", s);
        i = mp[s];
        final[f] = i;
        infinal[i] = 1;
        a[i] = Team();
        for (j = 0; j < n; j++) {
            scanf("%s%d%d", s, &sub, &t);
            a[i].submit(j, s[0], sub, t);
        }
    }

    for (int i = 0; i < k - 1; i++) {
        if (!(a[final[i]] < a[final[i + 1]])) {
            puts("Fake");
            return 0;
        }
    }


    Team& top = a[final[0]];
    Team& bottom = a[final[k - 1]];

    for (int i = 0; i < m; i++) {
        if (infinal[i]) continue;
        if (bottom < a[i]) continue;
        if (a[i] < top) continue;
        a[i].killAll();
        if (a[i] < top) continue;
        puts("Fake");
        return 0;
    }
    puts("Leaked");
    return 0;
}

G: God of Winds

题目大意

一个格子的风会导致4条边的风向和风力发生变化,给定网格中各条边的风向和风力,求哪些格子里有风,风向是顺时针还是逆时针,风力多大。

题解

如果我们令 f i , j 表示第i行第j列的格子的风力,那么存在式子:

r i , j = f i , j f i 1 , j c i , j = f i , j 1 f i , j

那么一个等式转化成两个不等式,就是差分约束系统了。
#include <cstdio>
typedef long long ll;
const int K = 512, N = K * K, M = N * 8;
int h[N], p[M], v[M], w[M], edge = 0;
int vis[N], id[K][K], cnt = 0;
int r[K][K], c[K][K];
ll f[N];
void add(int a, int b, int c) {
    p[++edge] = h[a]; v[edge] = b; w[edge] = c; h[a] = edge;
}

bool dfs(int x, ll now) {
    if (vis[x]) {
        return f[x] == now;
    }

    vis[x] = 1;
    f[x] = now;
    for (int i = h[x]; i; i = p[i])
        if (!dfs(v[i], now + w[i]))
            return false;

    return true;
}

int main() {
    int n, m, i, j;
    scanf("%d%d", &n, &m);

    for (i = 0; i < n; ++i)
        for (j = 0; j < m; ++j) {
            scanf("%d%d", &r[i][j], &c[i][j]);
            id[i][j] = cnt++;
        }

    for (i = 0; i < n; ++i)
        for (j = 0; j < m; ++j) {
            int self = id[i][j];

            int other = id[(i - 1 + n) % n][j];
            add(self, other, r[i][j]);
            add(other, self, -r[i][j]);

            other = id[i][(j - 1 + m) % m];
            add(other, self, c[i][j]);
            add(self, other, -c[i][j]);
        }

    for (i = 0; i < cnt; ++i)
        if (!vis[i])
            if (!dfs(i, 0))
                return puts("No"), 0;

    return puts("Yes"), 0;
}

H: Hilarious Cooking

题目大意

对于一个数列( n 2 10 9 ),已知部分元素(下标和元素本身),问是否存在一种填数列的方案使得数列各个元素的和=T。其中数列相邻两个元素的差的绝对值不能超过1。

题解

首先很容易想到求出数列的和的最大值和最小值再判断T是否在范围内(因为我们不需要具体的数列方案,而且满足题目要求的前提下我们连续(在非负整数集内)地调整数列元素和)。
既然差的绝对值不能超过1,那么两个点之间的元素只有可能是先45度上升(可能会有一格水平)再45度下降(最大值);或者相反(最小值)。那么这题就很简单了。

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 100005;
ll a[N], b[N];

ll count(ll low, ll high) {
    if (low > high) return 0;
    if (high < 0) return 0;
    if (low < 0) low = 0;
    return (low + high) * (high - low + 1) / 2;
}

ll compute_low(ll w, ll y1, ll y2) {
    ll h = y2 - y1;
    ll diff = (w + h) / 2;
    ll low = y2 - diff;
    if ((w + h) & 1) {
        return count(low, y1) + count(low, y2);
    } else {
        return count(low, y1) + count(low, y2) - max(low, 0ll);
    }
}

ll compute_high(ll w, ll y1, ll y2) {
    ll h = y2 - y1;
    ll diff = (w + h) / 2;
    ll high = y1 + diff;
    if ((w + h) & 1) {
        return count(y1, high) + count(y2, high);
    } else {
        return count(y1, high) + count(y2, high) - high;
    }
}


bool check(ll t, ll n, ll m, ll sum) {
    ll low = count(max(0ll, b[1] - (a[1] - 1)), b[1])
        + count(max(0ll, b[m] - (n - a[m])), b[m]) - sum;
    ll high = count(b[1], b[1] + (a[1] - 1))
        + count(b[m], b[m] + (n - a[m])) - sum;

    for (int i = 2; i <= m; ++i) {
        ll deltaY = abs(b[i] - b[i - 1]);
        ll deltaX = abs(a[i] - a[i - 1]);
        if (deltaY > deltaX)
            return false;

        ll mn = min(b[i - 1], b[i]);
        ll mx = max(b[i - 1], b[i]);
        low += compute_low(deltaX, mn, mx);
        high += compute_high(deltaX, mn, mx);
    }

    return low <= t && t <= high;
}

int main() {
    int n, m, i;
    ll t, sum = 0;
    scanf("%lld%d%d", &t, &n, &m);

    for (i = 1; i <= m; ++i) {
        scanf("%lld%lld", &a[i], &b[i]);
        sum += b[i];
    }

    puts(check(t, n, m, sum) ? "Yes" : "No");


    return 0;
}

猜你喜欢

转载自blog.csdn.net/huanghongxun/article/details/79510654