AtCoder Beginner Contest 138

AtCoder Beginner Contest 138

https://atcoder.jp/contests/abc138
今天做的,f非常有意思,状态设计很巧妙,是限定范围内的数位dp;e还不知道为什么错,感觉思路没问题。

A - Red or Not

#include <bits/stdc++.h>

using namespace std;

int main () {
    int n;
    string s;
    cin >> n >> s;
    if (n >= 3200)  cout << s;
    else    cout << "red";
}

B - Resistors in Parallel

#include <bits/stdc++.h>

using namespace std;

int main () {
    int n;
    string s;
    cin >> n >> s;
    if (n >= 3200)  cout << s;
    else    cout << "red";
}

C - Alchemist

#include <bits/stdc++.h>

using namespace std;

int main () {
    int n;
    cin >> n;
    vector <int> v;
    for (int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        v.push_back (x);
    }
    sort (v.begin (), v.end ());
    double sum = v[0];
    for (int i = 1; i < v.size (); i++) {
        sum += v[i], sum /= 2;
    }
    cout << sum;
}

D - Ki

树上差分。
建立dfs序之后根据子树size进行差分。

#include <bits/stdc++.h>
#define int long long

using namespace std;
const int N = 4e5 + 5;
int h[N], e[N], ne[N], idx;
int pos[N], sz[N], tmp[N], ans[N];
int n, q, d[N], cnt;

void add (int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void dfs (int u, int fa) {
    d[++cnt] = u, pos[u] = cnt;
    for (int i = h[u]; ~i; i = ne[i]) {
        int v = e[i];
        if (v == fa)    continue;
        dfs (v, u);
        sz[u] += sz[v];
    }
}

signed main () {
    memset (h, -1, sizeof h);
    cin >> n >> q;
    for (int i = 1; i < n; i++) {
        int a, b;
        cin >> a >> b;
        add (a, b), add (b, a);
    }
    for (int i = 1; i <= n; i++)    sz[i] = 1;
    dfs (1, -1);

    // for (int i = 1; i <= n; i++)    cout << d[i] << ' ';cout << endl;
    // for (int i = 1; i <= n; i++)    cout << pos[i] << ' ';cout << endl;
    // for (int i = 1; i <= n; i++)    cout << sz[i] << ' ';cout << endl;

    while (q --) {
        int x, y;
        cin >> x >> y;
        int l = pos[x], r = pos[x] + sz[x];
        // cout << l << ' ' << r << endl;
        tmp[l] += y, tmp[r] -= y;
    }
    for (int i = 1; i <= n; i++) {
        tmp[i] += tmp[i-1];
        ans[d[i]] = tmp[i];
    }
    for (int i = 1; i <= n; i++)    cout << ans[i] << ' ';
}

//因为回溯,所以需要反向边

E - Strings of Impurity

二分做法不知道为啥错(疑似超时?)
可以用二维数组预处理进行记录,类似dp的想法
记录匹配到模式串 \(s\) 的第 \(i\) 位时,字母 \(j\) 第一次出现的位置,就可以 \(O(1)\) 查找下标了

#include <bits/stdc++.h>

using namespace std;
const int N = 1e5 + 5;
int p[N][30]; //第i位字母j第一次出现的位置

int main () {
    string s, t;
    cin >> s >> t;
    int n = s.size (), m = t.size ();
    s = ' ' + s, t = ' ' + t;
    for (int i = 0; i < 26; i++)    p[n][i] = -1;
    p[n][s[n] - 'a'] = n;

    for (int i = n - 1; i; i--) {
        for (int j = 0; j < 26; j++)    p[i][j] = p[i+1][j]; //递推
        p[i][s[i] - 'a'] = i;
    }
    //match
    int cnt = 0, lst = 1;
    for (int i = 1; i <= m; i++) {
        if (p[1][t[i] - 'a'] == -1) {
            cout << -1;
            return 0;
        }
        if (p[lst][t[i] - 'a'] == -1)     cnt ++, lst = 1;
        lst = p[lst][t[i] - 'a'] + 1;
        if (lst > n)   cnt ++, lst = 1;
    }
    cout << 1ll * cnt * n + lst - 1 << endl;
}

F - Coincidence

限定范围内的数位dp。
这题可以加到视频里面

异或性质推导

\(y\mathrm{\,mod\,}x=y-x(x\leq y<2x\))

\[y \mathrm{\,mod\,} x = y-\lfloor {\frac yx} \rfloor x\rightarrow (y-\lfloor {\frac yx} \rfloor x)-(y-x)=x(1-\lfloor {\frac yx} \rfloor) \rightarrow \lfloor {\frac yx} \rfloor=1\rightarrow x\leq y<2x \]

来自群主%%%

\(y\bigoplus x=y+x-2(y\&x)\)

感性理解:异或是不进位加法,正常加法减去两倍进位就是异或。
证明:列真值表(?) 不懂数学证明www

由上述两条性质可得,\(x=x\&y\)

dp状态设计

注意状态设计

注意上下界:这个上下界就是框定了他的枚举范围。
如果没达到下界,就可以把当前枚举下界的该位设为0,否则就是 \(l\) 的该位;
如果没达到上界,就可以把当前枚举上界的该位设为1,否则就是 \(r\) 的该位;
同时,这个属性具有单调倾向,一旦没达到某个界,之后都不会再达到了。
比如,当前如果是dp[i][0][0],再往后都一直是0,0...

#include <bits/stdc++.h>
#define ll long long

using namespace std;
const int N = 70, mod = 1e9 + 7;
ll f[N][2][2]; //枚举到第i位, 1/0:是否达到下界L/上界R
ll l, r, L[N], R[N], ans;

ll dp (int u, int x1, int x2) {
    if (u <= 0)     return 1; //边界值!!防止数组越界
    if (f[u][x1][x2])   return f[u][x1][x2];
    ll l1 = 0, r1 = 1;
    if (x1 == 1)    l1 = L[u];
    if (x2 == 1)    r1 = R[u];
    for (ll x = l1; x <= r1; x++) {
        for (ll y = x; y <= r1; y++) {
            ll xx1 = 0, xx2 = 0;
            if (x1 && x == l1)  xx1 = 1;
            if (x2 && y == r1)  xx2 = 1;
            (f[u][x1][x2] += dp (u - 1, xx1, xx2)) %= mod;
        }
    }
    return f[u][x1][x2];
}

int main () {
    cin >> l >> r;
    int kl = 0, kr = 0;
    while (l > 0) {
        L[++kl] = (l & 1);
        l /= 2;
    }
    while (r > 0) {
        R[++kr] = (r & 1);
        r /= 2;
    }
    for (int i = kl; i <= kr; i++) {
        (ans += dp (i - 1, (i == kl), (i == kr))) %= mod;
    }
    cout << ans << endl;
}
posted @ 2023-02-22 21:31  Sakana~  阅读(34)  评论(0编辑  收藏  举报