比赛-ZR DAY2 (05 Aug, 2018)

A. 占领地区

离线处理。把主对角线与交矩形上方的那条边所在的直线的交点记录下来。这样对于每条副对角线,查询与它相交的主对角线这个问题就变成了一个区间求和问题。前缀和处理一下就可以了。应把副对角线分成两类讨论,因为可以发现在左上和右下的两条对角线“映射”到矩形上方的那条边所在的直线时,可能会重叠。做题的时候意识模糊去排了个序,其实没有必要,直接枚举位置就好了(桶排序的思想)。

#include <cstdio>
#include <algorithm>
#include <vector>

using namespace std;

typedef long long ll;

const int _N = 110000;

struct data {
    int l, r;
    data(int l = 0, int r = 0):
        l(l), r(r) { }
    bool operator == (const data &tmp) const
    {
        return l == tmp.l && r == tmp.r;
    }
    bool operator < (const data &tmp) const
    {
        return l != tmp.l ? l < tmp.l : r < tmp.r;
    }
};

ll N, M;
int SX[_N*2][2], len[2];
bool A[3100][3100], X[_N*2];
vector<data> Q[2];

void Update(int x, int y)
{
    if (1 <= x && x <= N && 1 <= y && y <= N) A[x][y] = true;
    return;
}

void Fun1()
{
    int i, j, t;
    ll ans = 0;
    for (i = 1; i <= M; ++i) {
        int x, y;
        scanf("%d%d", &x, &y);
        for (t = 1; t <= N; ++t) Update(t, x+y-t), Update(t, -(x-y-t));
    }
    for (i = 1; i <= N; ++i)
        for (j = 1; j <= N; ++j)
            if (A[i][j]) ++ans;
    printf("%lld\n", N*N-ans);
}

void Fun2()
{
    ll a, b, ans = 0;
    scanf("%lld%lld", &a, &b);
    ans += a+b >= N+1 ? N-(a+b-N)+1 : a+b-1;
    ans += b-a >= 0 ? N+(a-b-1)+1 : -(a-b-N);
    printf("%lld\n", N*N-ans+1);
}

void Fun3()
{
    int EX = N+100, i, j, t;
    ll ans = 0;
    for (i = 1; i <= M; ++i) {
        int a, b, g1_x, g1_y, g2_x, g2_y, k;
        scanf("%d%d", &a, &b);
        t = -(a-b-1);
        X[t+EX] = true;
        if (1 <= a+b-1 && a+b-1 <= N) {
            g1_x = a+b-1, g1_y = 1;
            g2_x = 1, g2_y = a+b-1;
            k = 0;
        } else {
            g1_x = N, g1_y = a+b-N;
            g2_x = a+b-N, g2_y = N;
            k = 1;
        }
        Q[k].push_back(data(-(g1_x-g1_y-1), -(g2_x-g2_y-1)));
    }
    for (i = 1; i <= N+EX; ++i) {
        SX[i][i&1] = SX[i-1][i&1]+X[i];
        SX[i][i&1^1] = SX[i-1][i&1^1];
        if (!X[i]) continue;
        t = 1-(i-EX)+1;;
        if (1 <= t && t <= N)
            ans += N-t+1;//-----------
        else if (1 <= i-EX && i-EX <= N)
            ans += N-(i-EX)+1;//--------
    }
    for (i = 0; i <= 1; ++i) {
        sort(Q[i].begin(), Q[i].end());
        len[i] = unique(Q[i].begin(), Q[i].end())-Q[i].begin();
    }
    for (i = 0; i <= 1; ++i) {
        for (j = len[i]-1; j >= 0; --j) {
            data p = Q[i][j];
            ans += p.r;//---------
            t = (p.r+EX)&1;
            ans -= SX[p.r+EX][t]-SX[p.l-1+EX][t];//----------
        }
    }
    printf("%lld\n", N*N-ans);
    return;
}

int main()
{
    scanf("%lld%lld", &N, &M);
    if (M <= 3005 && N <= 3005) { Fun1(); return 0; }
    if (M == 1) { Fun2(); return 0; }
    Fun3();
    return 0;
}

B. 配对

对每条边计算贡献。期望 = 权值 * (有贡献的方案 / 总方案) 。设一条边权 \(w\) , 连接了两个大小分别为 \(x\)\(y\) 的联通块,在大小为 \(x\) 的块中选 \(i\) 个男生 \(j\) 个女生,分析一波可以得出:
\(e = w * C(m, i) * C(m, j) * x^(i+j) * y^(m - i + m - j) * (min(i, m - j) + min(j, m - i))\)
\(t = i + j\) ,可以把多个期望合在一块儿贡献……各种预处理,时间复杂度降为 \(O(nm)\)

#include <cstdio>
#include <vector>
#include <algorithm>
#include <ctype.h>

using namespace std;

#define SC(a, b) (static_cast<a>(b))

typedef long long ll;

const int _N = 5200;
const ll MOD = 1e9+7;

struct edge {
    int v, w;
    edge(int v = 0, int w = 0):
        v(v), w(w) { }
};

vector<edge> G[_N];
int ans, N, M, Siz[_N], Mon[_N][_N], f[_N], J[_N], inv[_N];

void getnum(int &num)
{
    char tt;
    while (!isdigit(tt = getchar()));
    num = tt-'0';
    while (isdigit(tt = getchar()))
        num = (num<<3)+(num<<1)+tt-'0';
    return;
}

inline int mul(int a, int b) { return SC(int, SC(ll, a)*b%MOD); }

inline int add(int a, int b)
{
    ll tmp = SC(ll, a)+b;
    return SC(int, tmp > MOD ? tmp-MOD : tmp);
}

int mont(int a, int b)
{
    a %= MOD;
    int t = 1;
    while (b) {
        if (b & 1) t = mul(t, a);
        b >>= 1, a = mul(a, a);
    }
    return t;
}

void DFS(int p, int dad)
{
    Siz[p] = 1;
    for (int i = G[p].size()-1; ~i; --i) {
        edge g = G[p][i];
        if (g.v ^ dad) {
            DFS(g.v, p);
            Siz[p] += Siz[g.v];
            for (int j = 0; j <= (M<<1); ++j) {
                int tmp = mul(mul(Mon[Siz[g.v]][j], Mon[N-Siz[g.v]][(M<<1)-j]), f[j]);
                ans = add(ans, mul(tmp, g.w));
            }
        }
    }
    return;
}

inline int GetC(int dn, int up) { return mul(mul(J[dn], inv[dn-up]), inv[up]); }

int main()
{
    int i, j;
    getnum(N), getnum(M);
    for (i = 1; i < N; ++i) {
        int a, b, c;
        getnum(a), getnum(b), getnum(c);
        G[a].push_back(edge(b, c)), G[b].push_back(edge(a, c));
    }
    for (i = 1; i <= N; ++i) {
        Mon[i][0] = 1;
        for (j = 1; j <= (M<<1); ++j)
            Mon[i][j] = mul(Mon[i][j-1], i);
    }
    J[0] = 1;
    for (i = 1; i <= M; ++i) J[i] = mul(J[i-1], i);
    inv[M] = mont(J[M], MOD-2);
    for (i = M-1; i >= 0; --i) inv[i] = mul(inv[i+1], i+1);
    for (i = 0; i <= M; ++i)
        for (j = 0; j <= M; ++j)
            f[i+j] = add(f[i+j], mul(add(min(i, M-j), min(j, M-i)), mul(GetC(M, i), GetC(M, j))));
    DFS(1, -1);
    printf("%d\n", ans);
    return 0;
}

C. 导数卷积

太毒瘤了,不会做。极度痛苦地打完 \(O(n ^ 2 log n)\) 的 NTT 想拿 40 暴力分,结果 ZROJ 太快让 \(O(n ^ 3)\) 的纯暴力也过去了,血亏。然后 10 分的特殊样例没来得及看 Orz 。整洁不太懂……

猜你喜欢

转载自www.cnblogs.com/ghcred/p/9434365.html