2020 CCPC Wannafly Winter Camp Day1

A. 期望逆序对

考虑两个相邻的区间什么时候交换会更优,显然两个相邻的区间是否交换和其他区间的位置关系还是没有变化,那么就相当于冒泡排序的过程。分析可知按区间中点从小到大排序是最优的。剩下的就是两两枚举区间考虑它们获得逆序对的期望个数。可以发现是等差数列求和。

#include <bits/stdc++.h>

const int N = 5e3 + 7;
const int MOD = 998244353;

struct L {
    int l, r;
    bool operator < (const L &p) const {
        return l + r < p.l + p.r;
    }
} p[N];

int n, inv[N];

int qp(int a, int b = MOD - 2) {
    int ans = 1;
    while (b) {
        if (b & 1) ans = 1LL * ans * a % MOD;
        a = 1LL * a * a % MOD;
        b >>= 1;
    }
    return ans % MOD;
}

const int inv2 = qp(2);

void M(int &a) {
    if (a >= MOD) a -= MOD;
}

int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d%d", &p[i].l, &p[i].r);
    std::sort(p, p + n);
    /*for (int i = 0; i < n; i++) 
        printf("%d %d\n", p[i].l, p[i].r);*/
    for (int i = 0; i < n; i++)
        inv[i] = qp(p[i].r - p[i].l + 1);
    int ans = 0;
    for (int i = 0; i < n; i++) 
        for (int j = i + 1; j < n; j++) {
            int l = std::max(p[i].l, p[j].l), r = p[i].r;
            if (r < l) continue;
            r = std::min(p[i].r, p[j].r);
            int temp = 1LL * (r - p[j].l + l - p[j].l) * (r - l + 1) / 2 % MOD;
            M(temp += 1LL * (p[i].r - r) * (p[j].r - p[j].l + 1) % MOD);
            M(ans += 1LL * temp * inv[i] % MOD * inv[j] % MOD);
            //printf("%d %d %lld\n", i, j, 1LL * (r - p[j].l + l - p[j].l) * (r - l + 1) / 2 % MOD * all % MOD * inv[i] % MOD * inv[j] % MOD);
        }
    printf("%d\n", ans);
    return 0;
}
View Code

B. 密码学

倒着模拟即可。

C. 染色图

均分是最优的。

然后就是推推公式整除分块解决。

现有 $n$ 个点,均分成 $i$ 份,那么有 $n \% i$ 份有 $\lfloor \frac{n}{i} \rfloor + 1$ 个点,其他的都只有 $\lfloor \frac{n}{i} \rfloor$ 个点。设 $j=\lfloor \frac{n}{i} \rfloor$

$g(n, i)=\binom{i}{2}j^2+(n \% i)(i-1)j+\binom{n\%i}{2}$

$n \% i = n - ij$

整理之后就可以得到

$g(n,i)=\frac{1}{2}(ij^2+ij-2nj+n^2-n)$

即 $g(n,i)=\frac{1}{2}(i\lfloor \frac{n}{i} \rfloor^2+i\lfloor \frac{n}{i} \rfloor-2n\lfloor \frac{n}{i} \rfloor+n^2-n)$

求前缀和整除分块即可

D. 生成树

基尔霍夫矩阵的推广。把联通性改成边权权值,求出来的就是所有生成树的权值和。

即 $\forall i, j, i \neq j, A[i][j] = -x$ 表示 $i$ 和 $j$ 之间有一条权为 $x$ 为边。

$\forall i,A[i][i]=x$ 表示 $i$ 与其相邻的点之间的边权值和为 $x$。

$B$ 为 $A$ 删去第 $n$ 行第 $n$ 列得到的矩阵,$|B|$ 即为所有生成树的权值和。

这道题中,对于 $G_1$,$G_2$ 中同时出现的边,权值定为 $x$,只在 $G_1$ 中出现的边权值定为 $1$,其余边权值定为 $0$。

得到的基尔霍夫矩阵的行列式是一个 $n-1$ 次的多项式,$f(x)=\sum a_ix^i$,$a_i$ 表示用了 $i$ 条同时出现的边的生成树的数量,那么答案为 $f^{'}(1)=\sum i\times a_i$

矩阵求导法则(不知道哪里来的...)可得

$$f^{'}(1)=|B(1)|\sum_{i=1}^{n}\sum_{j=1}^{n}B^{'}(1)_{i,j}\times (B(1)^{-1})_{i,j}$$

需要做的就只有求 $B(1)$ 的行列式和逆矩阵,<del> $B^{'}(1)$ 就是每一项对 $x$ 求导后再代入 $1$ </del>。矩阵求导可能有误,但是直接这么写是对的orz...

#include <bits/stdc++.h>

const int N = 407;
const int MOD = 998244353;
int n;
char AG[N][N], BG[N][N];

struct Mat {
    int x, c;
} A[N][N];
int B1[N][N], C[N][2 * N];

int qp(int a, int b = MOD - 2) {
    int ans = 1;
    while (b) {
        if (b & 1) ans = 1LL * ans * a % MOD;
        a = 1LL * a * a % MOD;
        b >>= 1;
    }
    return ans;
}

void M(int &a) {
    if (a >= MOD) a -= MOD;
    if (a < 0) a += MOD;
}

int Gauss(int a[N][N], int n) {
    int ans = 1;
    for (int i = 1; i <= n; i++) {
        int mx = i;
        for (int j = i + 1; j <= n; j++)
            if (a[j][i] > a[i][i]) mx = j;
        if (mx != i) {
            std::swap(a[i], a[mx]);
            ans = -ans;
        }
        int inv = qp(a[i][i]);
        for (int k = i + 1; k <= n; ++k) {
            int c = 1LL * inv * a[k][i] % MOD;
            for (int j = i; j <= n; j++) 
                M(a[k][j] -= 1LL * c * a[i][j] % MOD);
        }
        ans = 1LL * ans * a[i][i] % MOD;
        M(ans += MOD);
    }
    return ans;
}

void Minv(int a[N][2 * N], int n) {
    for (int i = 1; i <= n; i++)
        a[i][i + n] = 1;
    for (int i = 1; i <= n; i++) {
        int mx = i;
        for (int j = i + 1; j <= n; j++)
            if (a[j][i] > a[i][i]) mx = j;
        if (mx != i) std::swap(a[i], a[mx]);
        int inv = qp(a[i][i]);
        for (int j = i; j <= 2 * n; j++)
            a[i][j] = 1LL * a[i][j] * inv % MOD;
        for (int j = 1; j <= n; j++) {
            if (i != j) {
                int r = a[j][i];
                for (int k = i; k <= 2 * n; k++) 
                    M(a[j][k] -= 1LL * r * a[i][k] % MOD);
            }
        }
    }
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%s", AG[i] + 1);
    for (int i = 1; i <= n; i++)
        scanf("%s", BG[i] + 1);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (i == j) continue;
            if (AG[i][j] == '1') {
                if (BG[i][j] == '1') {
                    A[i][j].x = -1;
                    A[i][i].x++;
                } else {
                    A[i][j].c = -1;
                    A[i][i].c++;
                }
            }
        }
    }
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            B1[i][j] = A[i][j].x + A[i][j].c;
    int ans = Gauss(B1, n - 1);
    for (int i = 1; i <= n - 1; i++)
        for (int j = 1; j <= n - 1; j++)
            C[i][j] = A[i][j].x + A[i][j].c, B1[i][j] = A[i][j].x;
    Minv(C, n - 1);
    int res = 0;
    for (int i = 1; i <= n - 1; i++)
        for (int j = 1; j <= n - 1; j++)
            M(res += 1LL * C[i][j + n - 1] * B1[i][j] % MOD);
    printf("%lld\n", 1LL * ans * res % MOD);
    return 0;
}
View Code

E. 树与路径

记 $A_i$ 表示以 $i$ 为根时的答案,$A_1$ 可以通过求 $\text{lca}$ 快速得到。考虑 $A_i$ 与 $A_{fa_i}$ 的关系。

一条路径如果不同时经过 $i$ 和 $fa_i$ ,那么对 $A_i$ 和 $A_{fa_i}$ 的贡献是一样的。

一条路径 $(u,v)$ 同时经过 $i$ 和 $fa_i$ 时,设路径长度为 $l$ ,$i$ 往下走到路径结尾的长度为 $x$,那么对 $A_i$ 的贡献为 $x(l-x)$,对 $A_{fa_i}$ 的贡献为 $(x+1)(l-x-1)$,那么 $A_{i} - A_{fa_i}=2x-l+1$。

记 $B_i=A_i-A_{fa_i}$,考虑一条路径 $(u,v)$ 考虑一条路径对路径上的点 $i$ 的 $B_i$ 的贡献。对 $u$ 的贡献为 $1-l$,对 $fa_u$ 的贡献为 $3-l$,以此类推可以得到,对路径上的点 $i$ 的 $B_i$ 的贡献为 $-2d_i+1-l+2d_u$,在每个节点处用一个二元组 $(a,b)$ 来表示 $B_i = a\times d_i + b$,那么一条路径就在 $u$ 处加上标记 $(-2,1-l+2d_u)$,在 $v$ 处加上标记 $(-2,1-l+2d_v)$,在其 $\text{lca}$ 处减去标记 $(-4,2-2l+2d_u+2d_v)$。相当于树上差分的做法,dfs 一遍得到 $B$,再 dfs 一遍得到 $A$ 即可。

#include <bits/stdc++.h>
#define ll long long
#define pii pair<int, ll>
#define fi first
#define se second

const int N = 3e5 + 7;

struct E {
    int v, ne;
} e[N << 1];
int head[N], cnt = 1, n, m;
int fa[N], son[N], dep[N];
int top[N], sz[N];
ll A[N], B[N];
std::pii p[N];

struct Edge {
    int u, v, lca, len;
} edge[N];

void add(int u, int v) {
    e[++cnt].v = v; e[cnt].ne = head[u]; head[u] = cnt;
    std::swap(u, v);
    e[++cnt].v = v; e[cnt].ne = head[u]; head[u] = cnt;
}

void dfs1(int u, int f) {
    dep[u] = dep[f] + 1;
    fa[u] = f;
    sz[u] = 1;
    for (int i = head[u]; i; i = e[i].ne) {
        int v = e[i].v;
        if (v == f) continue;
        dfs1(v, u);
        sz[u] += sz[v];
        if (sz[son[u]] < sz[v]) son[u] = v;
    }
}

void dfs2(int u, int tp) {
    top[u] = tp;
    if (!son[u]) return;
    dfs2(son[u], tp);
    for (int i = head[u]; i; i = e[i].ne) {
        int v = e[i].v;
        if (v == fa[u] || v == son[u]) continue;
        dfs2(v, v);
    }
}

int Lca(int u, int v) {
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) std::swap(u, v);
        u = fa[top[u]];
    }
    if (dep[u] > dep[v]) std::swap(u, v);
    return u;
}

std::pii operator + (const std::pii a, const std::pii b) {
    return std::pii(a.fi + b.fi, a.se + b.se);
}

void dfs3(int u) {
    for (int i = head[u]; i; i = e[i].ne) {
        int v = e[i].v;
        if (v == fa[u]) continue;
        dfs3(v);
        p[u] = p[u] + p[v];
    }
    B[u] = 1LL * p[u].fi * dep[u] + p[u].se;
}

void dfs4(int u) {
    if (u != 1) A[u] = A[fa[u]] + B[u];
    for (int i = head[u]; i; i = e[i].ne) {
        int v = e[i].v;
        if (v == fa[u]) continue;
        dfs4(v);
    }
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1, u, v; i < n; i++) {
        scanf("%d%d", &u, &v);
        add(u, v);
    }
    dep[0] = -1;
    dfs1(1, 0);
    dfs2(1, 1);
    for (int i = 1; i <= m; i++) {
        scanf("%d%d", &edge[i].u, &edge[i].v);
        int u = edge[i].u, v = edge[i].v, &lca = edge[i].lca, &len = edge[i].len;
        lca = Lca(u, v);
        len = dep[u] + dep[v] - 2 * dep[lca];
        A[1] += 1LL * (dep[u] - dep[lca]) * (dep[v] - dep[lca]);
        p[u].fi -= 2; p[u].se += 1 - len + 2 * dep[u];
        p[lca].fi += 2; p[lca].se -= 1 - len + 2 * dep[u];
        p[v].fi -= 2; p[v].se += 1 - len + 2 * dep[v];
        p[lca].fi += 2; p[lca].se -= 1 - len + 2 * dep[v];
    }
    dfs3(1);
    dfs4(1);
    for (int i = 1; i <= n; i++)
        printf("%lld\n", A[i]);
    return 0;
}
View Code

F. 乘法

排序,二分答案,check的时候枚举 A 中的元素,就能得到 B 中的元素应该满足的大小关系,可以用桶或者二分得到答案。

G. 圆凸包

显然,圆凸包只能由圆弧及它们之间的公切线形成。那么两两枚举得到公切线,把切点求个凸包,相邻两个点若在同一个圆上,那么它们之间就是一段圆弧,否则就是一条线段,这样得到答案,就能得到一个 "Wrong Answer"。

首先,如果一个切点被另一个圆包含,那么它肯定不能在凸包上,而且这种情况显然能出现。所以求出公切线之后,再枚举一遍所有圆,看看是否被其它圆包含,不包含才能加入要求凸包的点集中。

另外一种情况,如果只有两个圆,并且它们是内含的,使用上述算法将求不到切点,周长求出来也为 $0$。

所以先对圆按半径排序,如果一个小圆被其他大圆包含,则把它删掉。

#include <bits/stdc++.h>

#define db double

const db eps = 1e-9;
const db pi = acos(-1.0);
inline int sign(db k) { return k < -eps ? -1 : k > eps; }
inline int cmp(db k1, db k2) { return sign(k1 - k2); }

struct P {
  db x, y;
  P() {}
  P(db x, db y): x(x), y(y) {}
  P operator + (const P &p) const { return {x + p.x, y + p.y}; }
  P operator - (const P &p) const { return {x - p.x, y - p.y}; }
  P operator * (db k) const { return {x * k, y * k}; }
  P operator / (db k) const { return {x / k, y / k}; }
  bool operator < (const P &p) const {
    int c = cmp(x, p.x);
    return c ? c == -1 : cmp(y, p.y) == -1;
  }
  bool operator == (const P &p) const {
    return !cmp(x, p.x) && !cmp(y, p.y);
  }
  db distTo(const P &p) const { return (*this - p).abs(); }
  db alpha() { return atan2(y, x); }
  void read() { scanf("%lf%lf", &x, &y); }
  void print() { printf("%.10f %.10f\n", x, y); }
  db abs() { return sqrt(abs2()); }
  db abs2() { return x * x + y * y; }
  P rot(const db &k) { return P(x * cos(k) - y * sin(k), x * sin(k) + y * cos(k)); }
  P rot90() { return P(-y, x); }
  P unit() { return *this / abs(); }
  P normal() { return rot90() / abs(); }
  int quad() const { return sign(y) == 1 || (sign(y) == 0 && sign(x) >= 0); }
  P getdel() {if (sign(x) == -1 || (sign(x) == 0 && sign(y) == -1)) return (*this) * (-1); else return (*this);}
  db dot(const P &p) { return x * p.x + y * p.y; }
  db det(const P &p) { return x * p.y - y * p.x; }
};

struct L { // ps[0] -> ps[1]
  P ps[2];
  L() {}
  L(const P &p0, const P &p1) {
    ps[0] = p0; ps[1] = p1;
  }
  void read() {
    ps[0].read(); ps[1].read();
  }
  P &operator[](int i) { return ps[i]; }
  P dir() { return ps[1] - ps[0]; }
  bool include(const P &p) { return sign((ps[1] - ps[0]).det(p - ps[0])) > 0; }
  L push() { // push eps outawrd
    const db Eps = 1e-6;
    P delta = (ps[1] - ps[0]).normal() * Eps;
    return {ps[0] - delta, ps[1] - delta};
  }
};

struct circle {
  P o; db r;
  void read() {
    o.read();
    scanf("%lf", &r);
  }
  int inside(P k) {
    return cmp(r, o.distTo(k));
  }
  bool operator < (const circle &k) const {
    return cmp(r, k.r) == -1;
  }
};

db cross(P p1, P p2, P p3) {
  return ((p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y));
}

int crossOp(P p1, P p2, P p3) {
  return sign(cross(p1, p2, p3));
}

db cross(P p1, P p2) {
  return p1.x * p2.y - p1.y * p2.x;
}

// 判断 p1p2 和 q1q2 是否相交
bool chkLL(const P &p1, const P &p2, const P &q1, const P &q2) {
  db a1 = cross(q1, q2, p1), a2 = -cross(q1, q2, p2);
  return sign(a1 + a2) != 0;
}

bool chkLL(L l0, L l1) {
  return chkLL(l0[0], l0[1], l1[0], l1[1]);
}

// 直线交点
P isLL(const P &p1, const P &p2, const P &q1, const P &q2) {
  assert(chkLL(p1, p2, q1, q2));
  db a1 = cross(q1, q2, p1), a2 = -cross(q1, q2, p2);
  return (p1 * a2 + p2 * a1) / (a1 + a2);
}

P isLL(L l1, L l2) {
  return isLL(l1[0], l1[1], l2[0], l2[1]);
}
// 线段相交
bool intersect(db l1, db r1, db l2, db r2) {
  if (l1 > r1) std::swap(l1, r1); if (l2 > r2) std::swap(l2, r2);
  return !(cmp(r1, l2) == -1 || cmp(r2, l1) == -1);
}
bool isSS(const P &p1, const P &p2, const P &q1, const P &q2) {
  return intersect(p1.x, p2.x, q1.x, q2.x) && intersect(p1.y, p2.y, q1.y, q2.y)
         && crossOp(p1, p2, q1) * crossOp(p1, p2, q2) <= 0
         && crossOp(q1, q2, p1) * crossOp(q1, q2, p2) <= 0;
}
bool isSS_strict(const P &p1, const P &p2, const P &q1, const P &q2) {
  return crossOp(p1, p2, q1) * crossOp(p1, p2, q2) < 0
         && crossOp(q1, q2, p1) * crossOp(q1, q2, p2) < 0;
}

bool isSS(L l0, L l1) {
  return isSS(l0[0], l0[1], l1[0], l1[1]);
}

bool isSS_strict(L l0, L l1) {
  return isSS_strict(l0[0], l0[1], l1[0], l1[1]);
}

// 点在线段上判定
bool isMiddle(db a, db m, db b) {
  return sign(a - m) == 0 || sign(b - m) == 0 || (a < m != b < m);
}
bool isMiddle(const P &a, const P &m, const P &b) {
  return isMiddle(a.x, m.x, b.x) && isMiddle(a.y, m.y, b.y);
}
bool onSeg(const P &p1, const P &p2, const P &q) {
  return crossOp(p1, p2, q) == 0 && isMiddle(p1, q, p2);
}
bool onSeg_strict(const P &p1, const P &p2, const P &q) {
  return crossOp(p1, p2, q) == 0 && sign((q - p1).dot(p1 - p2)) * sign((q - p2).dot(p1 - p2)) < 0;
}

// p2 相对于 p0->p1 的位置
int ccw(P p0, P p1, P p2) {
  int k = crossOp(p0, p1, p2);
  if (k > 0) return 1; // 逆时针
  if (k < 0) return 2; // 顺时针
  int dot = sign((p1 - p0).dot(p2 - p0));
  if (dot < 0) return 3; // 反向延长线
  if (onSeg(p0, p1, p2)) return 5; // 线段上
  return 4; // 延长线
}

// 投影
P proj(const P &p1, const P &p2, const P &q) {
  P dir = p2 - p1;
  return p1 + dir * (dir.dot(q - p1) / dir.abs2());
}

// 反射
P reflect(const P &p1, const P &p2, const P &q) {
  return proj(p1, p2, q) * 2 - q;
}

// 最近点
db nearest(const P &p1, const P &p2, const P &q) {
  P h = proj(p1, p2, q);
  if (isMiddle(p1, h, p2)) return q.distTo(h);
  return std::min(p1.distTo(q), p2.distTo(q));
}

// 线段距离
db disSS(const P &p1, const P &p2, const P &q1, const P &q2) {
  if (isSS(p1, p2, q1, q2)) return 0;
  return std::min(std::min(nearest(p1, p2, q1), nearest(p1, p2, q2)), std::min(nearest(q1, q2, p1), nearest(q1, q2, p2)));
}

// 夹角
db rad(P p1, P p2) {
  return atan2l(p1.det(p2), p1.dot(p2));
}

db incircle(P p1, P p2, P p3) {
  db A = p1.distTo(p2);
  db B = p2.distTo(p3);
  db C = p3.distTo(p1);
  return sqrtl(A * B * C / (A + B + C));
}

// 多边形面积
db area(std::vector<P> ps) {
  db ans = 0;
  for (int i = 0, n = ps.size(); i < n; i++)
    ans += ps[i].det(ps[(i + 1) % n]);
  return std::fabs(ans / 2);
}

// 点包含 2: inside 1: onSeg 0: outside
int contain(std::vector<P> ps, P p) {
  int n = ps.size(), ret = 0;
  for (int i = 0; i < n; i++) {
    P u = ps[i], v = ps[(i + 1) % n];
    if (onSeg(u, v, p)) return 1;
    if (cmp(u.y, v.y) <= 0) std::swap(u, v);
    if (cmp(p.y, u.y) > 0 || cmp(p.y, v.y) <= 0) continue;
    ret ^= crossOp(p, u, v) > 0;
  }
  return ret * 2;
}
// 凸包
std::vector<P> convexHull(std::vector<P> ps) {
  int n = ps.size(); if (n <= 1) return ps;
  std::sort(ps.begin(), ps.end());
  std::vector<P> qs(n * 2); int k = 0;
  for (int i = 0; i < n; qs[k++] = ps[i++])
    while (k > 1 && crossOp(qs[k - 2], qs[k - 1], ps[i]) <= 0) --k;
  for (int i = n - 2, t = k; i >= 0; qs[k++] = ps[i--])
    while (k > t && crossOp(qs[k - 2], qs[k - 1], ps[i]) <= 0) --k;
  qs.resize(k - 1);
  return qs;
}
std::vector<P> convexHullNonStrict(std::vector<P> ps) {
  int n = ps.size(); if (n <= 1) return ps;
  std::sort(ps.begin(), ps.end());
  std::vector<P> qs(n * 2); int k = 0;
  for (int i = 0; i < n; qs[k++] = ps[i++])
    while (k > 1 && crossOp(qs[k - 2], qs[k - 1], ps[i]) < 0) --k;
  for (int i = n - 2, t = k; i >= 0; qs[k++] = ps[i--])
    while (k > t && crossOp(qs[k - 2], qs[k - 1], ps[i]) < 0) --k;
  qs.resize(k - 1);
  return qs;
}

// 点集直径
db convexDiameter(const std::vector<P> &ps) {
  int n = ps.size(); if (n <= 1) return 0;
  int is = 0, js = 0;
  for (int k = 1; k < n; k++)
    is = ps[k] < ps[is] ? k : is, js = ps[js] < ps[k] ? k : js;
  int i = is, j = js;
  db ret = ps[i].distTo(ps[j]);
  do {
    if ((ps[(i + 1) % n] - ps[i]).det(ps[(j + 1) % n] - ps[j]) >= 0)
      (++j) %= n;
    else
      (++i) %= n;
    ret = std::max(ret, ps[i].distTo(ps[j]));
  } while (i != is || j != js);
  return ret;
}

// convecCut
std::vector<P> convexCut(const std::vector<P> &ps, const P &q1, const P &q2) {
  std::vector<P> qs;
  int n = ps.size();
  for (int i = 0; i < n; i++) {
    P p1 = ps[i], p2 = ps[(i + 1) % n];
    int d1 = crossOp(q1, q2, p1), d2 = crossOp(q1, q2, p2);
    if (d1 >= 0) qs.push_back(p1);
    if (d1 * d2 < 0) qs.push_back(isLL(p1, p2, q1, q2));
  }
  return qs;
}
// min_dis sort first
db min_dis(std::vector<P> &ps, int l, int r) {
  if (r - l <= 5) {
    db ret = 1e5;
    for (int i = l; i < r; i++)
      for (int j = l; j < i; j++)
        ret = std::min(ret, ps[i].distTo(ps[j]));
    return ret;
  }
  int mid = l + r >> 1;
  db ret = std::min(min_dis(ps, l, mid), min_dis(ps, mid, r));
  std::vector<P> qs;
  for (int i = l; i < r; i++)
    if (cmp(fabs(ps[i].x - ps[mid].x), ret) <= 0) qs.push_back(ps[i]);
  std::sort(qs.begin(), qs.end(), [](P a, P b) -> bool { return cmp(a.y, b.y) < 0; });
  for (int i = 1; i < qs.size(); i++)
    for (int j = i - 1; j >= 0 && cmp(qs[j].y, qs[i].y - ret) >= 0; j--)
      ret = std::min(ret, qs[i].distTo(qs[j]));
  return ret;
}

// 圆的关系
int type(circle p, circle q) {
  P o1 = p.o, o2 = q.o;
  db r1 = p.r, r2 = q.r;
  db d = o1.distTo(o2);
  if (cmp(d, r1 + r2) == 1) return 4; // 相离
  if (cmp(d, r1 + r2) == 0) return 3; // 外切
  if (cmp(d, fabs(r1 - r2)) == 1) return 2; // 相交
  if (cmp(d, fabs(r1 - r2)) == 0) return 1; // 内切
  return 0;
}

int checkposCC(circle k1, circle k2) { // 返回两个圆的公切线数量
  if (cmp(k1.r, k2.r) == -1) std::swap(k1, k2);
  db dis = k1.o.distTo(k2.o);  int w1 = cmp(dis, k1.r + k2.r), w2 = cmp(dis, k1.r - k2.r);
  if (w1 > 0) return 4;
  if (w1 == 0) return 3;
  if (w2 > 0) return 2;
  if (w2 == 0) return 1;
  return 0;
}

std::vector<P> getCL(circle k1, P k2, P k3) { // 沿着 k2->k3 方向给出 , 相切给出两个
  P k = proj(k2, k3, k1.o); db d = k1.r * k1.r - (k - k1.o).abs2();
  if (sign(d) == -1) return {};
  P del = (k3 - k2).unit() * sqrt(std::max(0.0, d)); return {k - del, k + del};
}

std::vector<P> getCC(circle k1, circle k2) { // 沿圆 k1 逆时针给出 , 相切给出两个
  int pd = checkposCC(k1, k2); if (pd == 0 || pd == 4) return {};
  db a = (k2.o - k1.o).abs2(), cosA = (k1.r * k1.r + a - k2.r * k2.r) / (2 * k1.r * sqrt(std::max(a, (db)0.0)));
  db b = k1.r * cosA, c = sqrt(std::max((db)0.0, k1.r * k1.r - b * b));
  P k = (k2.o - k1.o).unit(), m = k1.o + k * b, del = k.rot90() * c;
  return {m - del, m + del};
}

std::vector<P> TangentCP(circle k1, P k2) { // 沿圆 k1 逆时针给出
  db a = (k2 - k1.o).abs(), b = k1.r * k1.r / a, c = sqrt(std::max((db)0.0, k1.r * k1.r - b * b));
  P k = (k2 - k1.o).unit(), m = k1.o + k * b, del = k.rot90() * c;
  return {m - del, m + del};
}

std::vector<L> TangentoutCC(circle k1, circle k2) {
  int pd = checkposCC(k1, k2); if (pd == 0) return {};
  if (pd == 1) {P k = getCC(k1, k2)[0]; return {(L) {k, k}};}
  if (cmp(k1.r, k2.r) == 0) {
    P del = (k2.o - k1.o).unit().rot90().getdel();
    return {(L){k1.o - del * k1.r, k2.o - del * k2.r}, (L) {k1.o + del*k1.r, k2.o + del*k2.r}};
  } else {
    P p = (k2.o * k1.r - k1.o * k2.r) / (k1.r - k2.r);
    std::vector<P> A = TangentCP(k1, p), B = TangentCP(k2, p);
    std::vector<L> ans; for (int i = 0; i < A.size(); i++) ans.push_back((L) {A[i], B[i]});
    return ans;
  }
}

std::vector<L> TangentinCC(circle k1, circle k2) {
  int pd = checkposCC(k1, k2); if (pd <= 2) return {};
  if (pd == 3) {P k = getCC(k1, k2)[0]; return {(L) {k, k}};}
  P p = (k2.o * k1.r + k1.o * k2.r) / (k1.r + k2.r);
  std::vector<P> A = TangentCP(k1, p), B = TangentCP(k2, p);
  std::vector<L> ans; for (int i = 0; i < A.size(); i++) ans.push_back((L) {A[i], B[i]});
  return ans;
}

std::vector<L> TangentCC(circle k1, circle k2) {
  int flag = 0; if (k1.r < k2.r) std::swap(k1, k2), flag = 1;
  std::vector<L> A = TangentoutCC(k1, k2), B = TangentinCC(k1, k2);
  for (L k : B) A.push_back(k);
  if (flag) for (L &k : A) std::swap(k[0], k[1]);
  return A;
}

db getarea(circle k1, P k2, P k3) {
  // 圆 k1 与三角形 k2 k3 k1.o 的有向面积交
  P k = k1.o; k1.o = k1.o - k; k2 = k2 - k; k3 = k3 - k;
  int pd1 = k1.inside(k2), pd2 = k1.inside(k3);
  std::vector<P> A = getCL(k1, k2, k3);
  if (pd1 >= 0) {
    if (pd2 >= 0) return cross(k2, k3) / 2;
    return k1.r * k1.r * rad(A[1], k3) / 2 + cross(k2, A[1]) / 2;
  } else if (pd2 >= 0) {
    return k1.r * k1.r * rad(k2, A[0]) / 2 + cross(A[0], k3) / 2;
  } else {
    int pd = cmp(k1.r, nearest(k2, k3, k1.o));
    if (pd <= 0) return k1.r * k1.r * rad(k2, k3) / 2;
    return cross(A[0], A[1]) / 2 + k1.r * k1.r * (rad(k2, A[0]) + rad(A[1], k3)) / 2;
  }
}

circle getcircle(P k1, P k2, P k3) {
  db a1 = k2.x - k1.x, b1 = k2.y - k1.y, c1 = (a1 * a1 + b1 * b1) / 2;
  db a2 = k3.x - k1.x, b2 = k3.y - k1.y, c2 = (a2 * a2 + b2 * b2) / 2;
  db d = a1 * b2 - a2 * b1;
  P o = (P) {k1.x + (c1 * b2 - c2 * b1) / d, k1.y + (a1 * c2 - a2 * c1) / d};
  return (circle) {o, k1.distTo(o)};
}

bool parallel(L l0, L l1) {
  return sign(l0.dir().det(l1.dir())) == 0;
}

bool orth(L l0, L l1) {
  return sign(l0.dir().dot(l1.dir())) == 0;
}

bool sameDir(L l0, L l1) {
  return parallel(l0, l1) && sign(l0.dir().dot(l1.dir())) == 1;
}

bool cmp(P a, P b) {
  if (a.quad() != b.quad()) {
    return a.quad() < b.quad();
  } else {
    return sign(a.det(b)) > 0;
  }
}

bool operator < (L l0, L l1) {
  if (sameDir(l0, l1)) {
    return l1.include(l0[0]);
  } else {
    return cmp(l0.dir(), l1.dir());
  }
}

bool check(L u, L v, L w) {
  return w.include(isLL(u, v));
}

const int N = 1e3 + 7;
L que[N];

std::vector<L> halfPlaneIS(std::vector<L> &l) {
  std::sort(l.begin(), l.end());
  int head = 0, tail = 0;
  for (int i = 0; i < l.size(); i++) {
    if (i && sameDir(l[i], l[i - 1])) continue;
    while (tail - head > 1 && !check(que[tail - 2], que[tail - 1], l[i])) tail--;
    while (tail - head > 1 && !check(que[head + 1], que[head], l[i])) head++;
    que[tail++] = l[i];
  }
  while (tail - head > 2 && !check(que[tail - 2], que[tail - 1], que[0])) tail--;
  while (tail - head > 2 && !check(que[1], que[0], que[tail - 1])) head++;
  std::vector<L> ans;
  for (int i = head; i < tail; i++)
    ans.push_back(que[i]);
  return ans;
}

std::vector<circle> A;
std::vector<P> B;

int getpos(P p) {
  int pos = -1;
  for (int i = 0; i < A.size(); i++) 
    if (A[i].inside(p) == 0) 
      return i;
  return -1;
}

void solve() {
  int n;
  scanf("%d", &n);
  A.clear(); B.clear();
  for (int i = 0; i < n; i++) {
    circle p;
    p.read();
    A.push_back(p);
  }
  std::sort(A.begin(), A.end());
  n = 0;
  for (int i = 0; i < A.size(); i++) {
    bool flag = 1;
    for (int j = i + 1; j < n; j++) {
      if (cmp(A[j].o.distTo(A[i].o), A[j].r - A[i].r) <= 0) {
        flag = 0;
        break;
      }
    }
    if (flag)
      A[n++] = A[i];
  }
  A.resize(n);
  if (n == 1) {
    printf("%.10f\n", 2 * pi * A[0].r);
    return;
  }
  for (int i = 0; i < n; i++) {
    for (int j = i + 1; j < n; j++) {
      std::vector<L> ps = TangentCC(A[i], A[j]);
      for (auto l: ps) {
        for (int k = 0; k < 2; k++) {
          bool flag = 1;
          /*for (int z = 0; z < n; z++)
            if (A[z].inside(l[k]) == 1) {
              flag = 0;
              break;
            }*/
          if (flag)
            B.push_back(l[k]);
        }
      }
    }
  }
  B = convexHull(B);
  db ans = 0;
  for (int i = 0; i < B.size(); i++) {
    P p1 = B[i], p2 = B[(i + 1) % B.size()];
    int pos1 = getpos(p1), pos2 = getpos(p2);
    if (pos1 != pos2) {
      ans += p1.distTo(p2);
      continue;
    }
    db rad1 = (p1 - A[pos1].o).alpha(), rad2 = (p2 - A[pos1].o).alpha();
    if (cmp(rad1, rad2) == 1)
      rad2 += 2 * pi;
    ans += (rad2 - rad1) * A[pos1].r;
  }
  printf("%.10f\n", ans);
}

int main() {
  int T;
  scanf("%d", &T);
  while (T--) 
    solve();
  return 0;
}
View Code

H. 最大公约数

$y$ 必须为 $k$ 的倍数,否则 $\gcd(k,y) = \gcd(\gcd(k,y),y)$

对于 $[1...\lfloor \frac{n}{k} \rfloor]$ 中的所有质数 $p$,$y$ 必须为 $p$ 的倍数,否则 $\gcd(pk,y)=\gcd(k,y)$

所以答案就是 $k\prod \limits_{i=1}^{m} p_i$

I. K小数查询

题解写的树套树,之后和segment tree beats再一起研究研究...

直接分块做,每个块里维护一个排好序的vector用来询问时二分,<del>再用一个数组 $\text{mn}$ 来记录每一块的修改标记,刚开始都是块内的最大值,整块就直接对 $\text{mn}$ 取 $min$</del>,用一个数组维护块内最大值,对块与 $x$ 取 $\min$ 即为最大值变得小于 $x$,打上标记即可。非完整块就暴力 rebuild 。

询问就二分答案,查找每一块里根据块内最大值来得到有多少数比 $mid$ 小,看看加起来是否不小于 $k$。

块大小设为 $\sqrt {n\log n}$ 即可。

#include <bits/stdc++.h>

const int N = 1e5 + 7;
const int BLOCK = 4e3 + 7;

int B, n, m, a[N], mn[BLOCK], belong[N], l[BLOCK], r[BLOCK], num;

std::vector<int> vec[BLOCK];

void build() {
    B = sqrt(n * log2(n));
    num = n / B;
    if (n % B) num++;
    for (int i = 1; i <= num; i++) {
        l[i] = (i - 1) * B + 1;
        r[i] = std::min(i * B, n);
        for (int j = l[i]; j <= r[i]; j++) {
            belong[j] = i;
            vec[i].push_back(a[j]);
        }
        std::sort(vec[i].begin(), vec[i].end());
        mn[i] = vec[i].back();
    }
}

void rebuild(int pos) {
    vec[pos].clear();
    for (int i = l[pos]; i <= r[pos]; i++)
        a[i] = std::min(a[i], mn[pos]), vec[pos].push_back(a[i]);
    std::sort(vec[pos].begin(), vec[pos].end());
    mn[pos] = vec[pos].back();
}

void update(int x, int y, int v) {
    int p = belong[x], q = belong[y];
    if (p == q) {
        for (int i = x; i <= y; i++)
            a[i] = std::min(a[i], v);
        rebuild(p);
        return;
    }
    for (int i = x; i <= r[p]; i++)
        a[i] = std::min(a[i], v);
    rebuild(p);
    for (int i = l[q]; i <= y; i++)
        a[i] = std::min(a[i], v);
    rebuild(q);
    for (int i = p + 1; i < q; i++)
        mn[i] = std::min(mn[i], v);
}

int count(int x, int y, int k) {
    int p = belong[x], q = belong[y];
    if (p == q) {
        int ans = 0;
        for (int i = x; i <= y; i++)
            if (a[i] <= k) ans++;
        return ans;
    }
    int ans = 0;
    for (int i = x; i <= r[p]; i++)
        if (a[i] <= k) ans++;
    for (int i = l[q]; i <= y; i++)
        if (a[i] <= k) ans++;
    for (int i = p + 1; i < q; i++) {
        if (mn[i] <= k) {
            ans += r[i] - l[i] + 1;
            continue;
        }
        ans += std::upper_bound(vec[i].begin(), vec[i].end(), k) - vec[i].begin();
    }
    return ans;
}

int query(int x, int y, int k) {
    int l = 1, r = n, ans = 0;
    int p = belong[x], q = belong[y];
    rebuild(p);
    if (p != q) rebuild(q);
    while (l <= r) {
        int mid = l + r >> 1;
        if (count(x, y, mid) >= k) ans = mid, r = mid - 1;
        else l = mid + 1;
    }
    return ans;
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%d", a + i);
    build();
    for (int i = 1; i <= m; i++) {
        int opt, l, r, x;
        scanf("%d%d%d%d", &opt, &l, &r, &x);
        if (opt == 1)
            update(l, r, x);
        else 
            printf("%d\n", query(l, r, x));
    }
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/Mrzdtz220/p/12227324.html