Loading

2024杭电多校第一场

1005 博弈(hdu7437

好吧赛时根本没写到它()

开始看题以为得一步一步算(是什么让我有这么离谱的思路.jpg),看了官方题解才发现自己的愚蠢呜呜,就是说有没有一种可能,A和B前 \(\lfloor n/2 \rfloor\) 位的字符串情况是“等价”的?可以视为把A的字符直接换给B,两方取到每种结果的概率实际上一样。。。 但对于A而言,平局并不计入胜利情况,单独考虑平局即可。

于是开始快乐分类讨论如下:

\(n\) 为偶数时,设平局概率为 \(p\) ,A获胜的概率即为 \((1-p)/2\)\(n\) 为奇数时,由于A比B多取一个字符,若前 \((n-1)/2\) 个字符相同,则A必胜,设前 \((n-1)/2\) 个字符相同的概率为 \(p\) ,A获胜的概率为 \((1+p)/2\) .

至于如何计算平局的概率,本菜鸡的求法极其抽象()将情况总数看作“长为 \(2n\) 的序列,每个位置在原字符集里挑选一种字符的方案数”,其中前 \((n-\lfloor n/2 \rfloor)\) 位属于A,后 \(\lfloor n/2 \rfloor\) 属于B,这样就避免了重复情况,可以用高中的排列组合知识 \(O(1)\) 计算;用cnt数组记录每个字母出现的次数,前 \(\lfloor n/2 \rfloor\) 个字符相同的概率可同理看作“长为 \(\lfloor n/2 \rfloor\) 的序列,在字符集里挑选的方案数”,其中第 \(i\) 种字符只有 \(cnt[i]/2\) 个,仍然可以 \(O(1)\) 求出。此处注意 \(n\) 的奇偶情况略有不同,特判一下就好,问题不大。

主要代码如下:(还是好长一坨,QwQ)

    int n;
    scanf("%d", &n);
    int s = 0;
    for(int i = 1; i <= n; i++) {
        char c = getchar();
        while(c < 'a' || c > 'z') c = getchar();
        int h;
        scanf("%d", &h);
        s += h;
        cnt[c - 'a'] += h;
    }
    if(s & 1) {
        int ok = 1;
        for(int i = 0; i < 26; i++) {
            if(cnt[i] & 1) {
                if(ok) {
                    ok = 0;
                } else {
                    ok = 1;
                    break;
                }
            }
        }
        if(ok) {
            printf("%lld\n", inv(2));
            continue;
        }
        ll x = 1, y = 1, ys = s / 2;
        for(int i = 0; i < 26; i++) {
            if(!cnt[i]) continue;
            x = x * c(s, cnt[i]) % mo;
            s -= cnt[i];
        }
        for(int i = 0; i < 26; i++) {
            if(cnt[i] <= 1) continue;
            cnt[i] /= 2;
            y = y * c(ys, cnt[i]) % mo;
            ys -= cnt[i];
        }
        ll p = y * inv(x) % mo;
        printf("%lld\n", (1 + p) * inv(2) % mo);
    } else {
        int ok = 0;
        for(int i = 0; i < 26; i++) {
            if(cnt[i] & 1) {
                ok = 1;
                break;
            }
        }
        if(ok) {
            printf("%lld\n", inv(2));
            continue;
        }
        ll x = 1, y = 1, ys = s / 2;
        for(int i = 0; i < 26; i++) {
            if(!cnt[i]) continue;
            x = x * c(ys * 2, cnt[i]) % mo;
            y = y * c(ys, cnt[i] / 2) % mo;
            ys -= cnt[i] / 2;
        }
        ll p = y * inv(x) % mo;
        printf("%lld\n", (1 + mo - p) * inv(2) % mo);
    }

1006 序列立方 (hdu7438

一言以蔽之:人类智慧题(bushi

需要求出序列中每个非空子序列出现次数的立方值的和(什么绕口令),这个立方值看上去十分抽象也不好处理,于是换一种思路:在序列中可重复地取三个子序列,它们相同的情况总数。

考虑动态规划,\(dp[i][j][k]\) 表示已经选了三个相同的子序列,其结尾分别在第 \(i,j,k\) 位,故 \(a[i]=a[j]=a[k]\) 时满足条件。使用前缀和数组 \(s[i][j][k]\) 表示以 \(i,j,k\) 位结尾的方案总数,有 \(a[i]=a[j]=a[k]\)\(dp[i][j][k]=s[i-1][j-1][k-1]\) ,前缀和可用容斥思想维护。

代码如下,注意代码中用ans统计答案,可略去dp数组。

    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    for(int i = 0; i <= n; i++) {
        for(int j = 0; j <= n; j++) {
            s[i][j][0] = s[i][0][j] = s[0][i][j] = 1;
        }
    }
    ll ans = 0;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            for(int k = 1; k <= n; k++) {
                ll ss = (s[i - 1][j][k] + s[i][j - 1][k] + s[i][j][k - 1]) % mo;
                ss -= (s[i - 1][j - 1][k] + s[i - 1][j][k - 1]
                       + s[i][j - 1][k - 1]) % mo;
                ss = (ss + mo) % mo;
                ss += s[i - 1][j - 1][k - 1];
                ss %= mo;
                if(a[i] == a[j] && a[i] == a[k]) {
                    ans += s[i - 1][j - 1][k - 1];
                    ans %= mo;
                    ss += s[i - 1][j - 1][k - 1];
                    ss %= mo;
                }
                s[i][j][k] = ss;
            }
        }
    }
    printf("%lld\n", ans);

1012 并 (hdu7444

赛时死活过不了,赛后全删掉重写了一遍就过了,真好,有一只狸花猫释怀地似了。

看上去非常有扫描线风味的题,其实并没有那么麻烦,由于 \(n \le 2000\)\(n^2\) 做法完全能过,我自己就写的差分,赛后听xht说也可以用类似扫描线的存边方式维护+统计。至于期望部分就不难了(我没挂在期望上那就是不难),前面的差分or扫描线统计每块面积被覆盖的次数,设 \(cov[i]\) 表示恰好被 \(i\) 个矩形所覆盖的总面积,选取 \(k\) 个矩形时,期望等于概率(1-没取到该面积的不合法情况数/总情况数,类似容斥思想)乘以面积,总和即 \(\sum\limits_{i=1}^{n}(\binom{n}{k}-\binom{n-i}{k})*cov[i]/\binom{n}{k}\).

以下为差分代码

    int n;
    scanf("%d", &n);
    c[0][0] = 1;
    for(int i = 1; i <= n; i++) {
        c[i][0] = 1;
        for(int j = 1; j <= i; j++) {
            c[i][j] = c[i - 1][j] + c[i - 1][j - 1];
            if(c[i][j] > mo) c[i][j] -= mo;
        }
    }
    for(int i = 1; i <= n; i++) {
        int x0, y0, xx, yy;
        scanf("%d%d%d%d", &x0, &y0, &xx, &yy);
        x[i * 2 - 1] = x0;
        x[i * 2] = xx;
        y[i * 2 - 1] = y0;
        y[i * 2] = yy;
        sq[i] = {x0, y0, xx, yy};
    }
    sort(x + 1, x + n * 2 + 1);
    int nx = unique(x + 1, x + n * 2 + 1) - x - 1;
    sort(y + 1, y + n * 2 + 1);
    int ny = unique(y + 1, y + n * 2 + 1) - y - 1;
    for(int i = 1; i <= n; i++) {
        sq[i].x0 = lower_bound(x + 1, x + nx + 1, sq[i].x0) - x;
        sq[i].x = lower_bound(x + 1, x + nx + 1, sq[i].x) - x;
        sq[i].y0 = lower_bound(y + 1, y + ny + 1, sq[i].y0) - y;
        sq[i].y = lower_bound(y + 1, y + ny + 1, sq[i].y) - y;
        d[sq[i].x0][sq[i].y0]++;
        d[sq[i].x0][sq[i].y]--;
        d[sq[i].x][sq[i].y0]--;
        d[sq[i].x][sq[i].y]++;
    }
    for(int i = 1; i < nx; i++) {
        for(int j = 1; j < ny; j++) {
            ss[j] = ss[j - 1] + d[i][j];
            d[i][j] += d[i - 1][j] + ss[j - 1];
            s[d[i][j]] += (x[i + 1] - x[i]) * (y[j + 1] - y[j]) % mo;
            s[d[i][j]] %= mo;
        }
    }
    for(int k = 1; k <= n; k++) {
        ll ans = 0;
        for(int i = 1; i <= n; i++) {
            ans += s[i] * (c[n][k] - c[n - i][k] + mo) % mo;
            ans %= mo;
        }
        ans = ans * inv(c[n][k]) % mo;
        printf("%lld\n", ans);
    }

以下为扫描线代码,xht赛时非常顺利地过了,不像我)<-但我认为问题出在我而不是差分上

    int n;
    scanf("%d", &n);
    c[0][0] = 1;
    for(int i = 1; i <= n; i++) {
        c[i][0] = 1;
        for(int j = 1; j <= i; j++) {
            c[i][j] = c[i - 1][j] + c[i - 1][j - 1];
            if(c[i][j] > mo) c[i][j] -= mo;
        }
    }
    for(int i = 1; i <= n; i++) {
        int x0, y0, xx, yy;
        scanf("%d%d%d%d", &x0, &y0, &xx, &yy);
        x[i * 2 - 1] = x0;
        x[i * 2] = xx;
        a[i * 2 - 1] = {x0, xx, y0, 1};
        a[i * 2] = {x0, xx, yy, -1};
    }
    n *= 2;
    sort(x + 1, x + n + 1);
    int sz = unique(x + 1, x + n + 1) - x - 1;
    for(int i = 1; i <= n; i++) {
        a[i].l = lower_bound(x + 1, x + sz + 1, a[i].l) - x;
        a[i].r = lower_bound(x + 1, x + sz + 1, a[i].r) - x;
    }
    sort(a + 1, a + n + 1, cmp);
    for(int i = 1; i < n; i++) {
        for(int j = a[i].l; j < a[i].r; j++) {
            cov[j] += a[i].k;
        }
        for(int j = 1; j < sz; j++) {
            ans[cov[j]] += (x[j + 1] - x[j]) * (a[i + 1].h - a[i].h) % mo;
            ans[cov[j]] %= mo;
        }
    }
    n /= 2;
    for(int k = 1; k <= n; k++) {
        ll s = 0;
        for(int i = 1; i <= n; i++) {
            s += ans[i] * (c[n][k] - c[n - i][k] + mo) % mo;
            s %= mo;
        }
        s = s * inv(c[n][k]) % mo;
        printf("%lld\n", s);
    }
posted @ 2024-07-31 16:07  Aderose_yr  阅读(165)  评论(0编辑  收藏  举报