「泛做题解」曾经的 BZOJ 题解汇总

注:此博客写于 2017.12

一个利益至上的OJ.

坚决抵制

SDOI

BZOJ3122-[SDOI2013]随机数生成器 给定参数 \(a,b,X[1]\) ,序列 \(X\)\(X[i+1] = (a \times X[i] + b) \mod p\) 得到。问第一次在序列 \(X\) 中出现 \(t\) 的位置, \(p\) 是一个给定的质数。或者永远不会出现。

分类讨论,大小步算法。 先要特判一波。 \(X[1] = t\)\(a = 0\) 时直接输出。 \(a = 1\) 时,就是一个裸的 \(exgcd\) 。接下来就怒推一发公式。。。

显然可以暴力展开,得到:

\[X[n] = aX[n-1] + b = a(aX[n-2] + b) + b = ... = a ^ {n-1} X[1] + (a ^ 0 + a ^ 1 + ... + a ^ {n-2}) b\]

随便化简一下:

\[a ^ {n-1} = \frac {X[n] - \frac {a ^ {n-1} -1} { a - 1} \times b} {X[1]} \]

跑个BSGS就好了。小心爆int。

BZOJ1927-[SDOI2010]星际竞速 给定一个DAG,要求用若干条不相交路径覆盖所有的点,给定所有边的代价,以及所有点作为路径的第一个点的代价。问最小的代价之和。

最小路径覆盖,费用流,拆点。 首先考虑 \(S\)\(i\) 连上 \((1,ai)\) 的边,\(i\)\(T\)\((1,0)\) 。此时它可以选择 一个 后继节点,于是考虑增加一个点 \(i+n\) ,需要 \(S \rightarrow n+i, (1,0)\) ,考虑删除这个点 \(i\) ,对于 \(i \rightarrow j\)\((1,w_{i,j})\) 。表示将 \(j\) 作为第一个点的代价 可以是 \(w_{i,j}\) 。以上是思考过程。

正确性?考虑到每个点的流量只有两种可能来源,逆推即可得到一种对应方案。同时每一种方案也对应一种可行的流。所以最小费用即为最小答案。

BZOJ1974-[SDOI2010]auction代码拍卖会 一个 \(10\) 进制表示的正整数,如果从左到右,每一位的数字都不小于前一位的数字,则被称为上升数。 给出正整数 \(n\)\(K\) ,求有多少个长度恰好为\(n\) 的上升数是 \(K\) 的倍数,答案对 \(999911659\) 取模。 \(n \leq 10^{18}, K \leq 500\)

动态规划。 如果纵向切割,显然很难搞。考虑横向切割,不难发现这个数可以拆分为不超过 \(9\) 个,\(1,11,111,1111,..\) 的和。然后,这些数模 \(K\) 很显然是存在循环的。定义 \(f[i][j][k]\) 表示前 \(i\) 组中,选择了 \(j\) 个数,模意义下为 \(K\) 的方案数。

需要注意的是 \(\frac {10^{n-1} - 1} 9\) 要特殊处理,同时注意 \(K\) 等于 \(1\) 的情况

ZJOI

BZOJ1414-[ZJOI2009]对称的正方形 给定一个 \(n \times m\) 的矩阵。问存在多少子矩阵,使得它上下对称且左右对称。
\(n,m \leq 10^3\)

HASH乱搞。 显然,对于每一个位置,我们可以二分求出一个极大子矩阵。判断就可以用二维HASH,注意方向,小心不要写成旋转对称QAQ。新技能get!二维哈希。

我们可以弄两个不同的大素数 \(p,q\)\((i,j)\) 就要乘上 \(p ^ i \times q ^ j\) 。用unsigned int,然后自然溢出就好了。。

不放心的话,可以用unsigned long long。相当于双模数HASH

BZOJ2111-[ZJOI2010]Perm 排列计数 计算 \(1,2,..,n\) 的排列在大根堆中有多少种方案。

组合,动态规划。 首先求出每个位置的 \(size\) ,定义 \(f[i]\) 为以第 \(i\) 个位置为根的方案数。如果它有两个儿子,那么 \(f[i] = \binom {size[i]-1} {size[lc]} \times f[lc] \times f[rc]\)

注意,这里的组合数的 \(n,m\) 有可能超过 \(MOD\) 。所以需要使用 Lucas定理

SHOI

BZOJ1018-[SHOI2008]堵塞的交通traffic 给定一个 \(2*n\) 的网格图,要求支持加边,删边,求两点的连通。

线段树。 因为是线段上的操作,所以很显然可以用线段树来维护。

不难发现,所有的路径都像这样:向往另一端走,然后转回来,超过之后再转回来。所以就要维护前缀,后缀和两端的最短路,更新类似于floyd。

SCOI

BZOJ1078-[SCOI2008]斜堆 给定一棵斜堆,要求还原插入序列。保证是一个置换。

斜堆的性质。 题解 似乎还要再想想。

BZOJ2333-[SCOI2011]棘手的操作\(n\) 个节点,每个点有一个权值,要求支持以下操作:

  • U x y 加一条边,连接第 \(x\)个节点和第 \(y\) 个节点
  • A1 x v 将第 \(x\) 个节点的权值增加 \(v\)
  • A2 x v 将第 \(x\) 个节点所在的连通块的所有节点的权值都增加 \(v\)
  • A3 v 将所有节点的权值都增加 \(v\)
  • F1 x 输出第 \(x\) 个节点当前的权值
  • F2 x 输出第 \(x\) 个节点所在的连通块中,权值最大的节点的权值
  • F3 输出所有节点中,权值最大的节点的权值

左偏树,懒标记。 看到操作 U 和最值,不难想到要使用可并堆维护。由于 F3 操作的存在,我们需要维护一个 set 用于保存所有堆顶的值。而 A2 可以用线段树的懒标记实现。A3就搞一个简单的全局增量。A1操作需要删除原有的节点,再加入新的。查询的时候注意要路径上的所有点pushdown,因为左偏树的高度是期望 \(O(\log)\) 的,所有往上找父亲的复杂度是对的。

代码其实挺难写的,要注意 set 修改的时候要仔细,同时A1A2操作都需要修改。

void pushdown(int x) {
    if (!h[x].lazy) return;
    if (h[x].l) { h[h[x].l].val += h[x].lazy; h[h[x].l].lazy += h[x].lazy; }
    if (h[x].r) { h[h[x].r].val += h[x].lazy; h[h[x].r].lazy += h[x].lazy; }
    h[x].lazy = 0;
}

void ALLpushdown(int x) {
    if (h[x].fa) ALLpushdown(h[x].fa);
    pushdown(x);
}

inline int getroot(int x) {
    while (h[x].fa) x = h[x].fa;
    return x;
}

int merge(int x, int y) {
    if (!x || !y) return x + y;
    pushdown(x); pushdown(y);
    if (h[x].val < h[y].val) swap(x, y);
    h[x].r = merge(h[x].r, y); h[h[x].r].fa = x;
    if (d[h[x].l] < d[h[x].r]) swap(h[x].l, h[x].r);
    d[x] = d[h[x].r] + 1;
    return x;
}

int clear(int x) {
    int t = merge(h[x].l, h[x].r), f = h[x].fa;
    h[x].fa = h[x].l = h[x].r = 0;
    if (x == h[f].l) h[f].l = t;
    else h[f].r = t;
    h[t].fa = f;
    return getroot(t);
}

int main() {
    n = read();
    rep (i, 1, n) {
        h[i].val = read();
        s.insert(h[i].val);
    }
    Q = read(); d[0] = -1;
    while (Q--) {
        op = readchar();
        if (op == 'U') {
            u = getroot(read()); v = getroot(read());
            if (u != v) {
                if (merge(u, v) == u) s.erase(s.find(h[v].val));
                else s.erase(s.find(h[u].val));
            }
        }
        else if (op == 'A'){
            op2 = readchar();
            if (op2 == '1') {
                x = read(); y = read();
                ALLpushdown(x);
                s.erase(s.find(h[getroot(x)].val));
                h[x].val += y;
                s.insert(h[merge(x, clear(x))].val);
            }
            else if (op2 == '2') {
                x = getroot(read()); y = read();
                s.erase(s.find(h[x].val));
                s.insert(h[x].val += y);
                h[x].lazy += y;
            }
            else delta += read();
        }
        else {
            op2 = readchar();
            if (op2 == '1') {
                x = read();
                ALLpushdown(x);
                writeln(h[x].val + delta);
            }
            else if (op2 == '2') {
                x = getroot(read());
                writeln(h[x].val + delta);
            }
            else writeln(*(--s.end()) + delta);
        }
    }
    return 0;
}

BZOJ1294-[SCOI2009]围豆豆Bean 在一个 \(n \times m\) 的矩阵中,有 \(D\) 个豆豆,都有一定的分值,还有一些障碍物。如果一个位置没有障碍,而且没有豆豆,就可以进入。要求从某个位置出发,最后回到这个位置,得到的价值为围住的豆豆数量,减去走的步数。求得到的价值最大为多少。
\(n, m \leq 10, D \leq 9\)

SPFA,状压。 考虑枚举每一个点作为起点,\(f[i][j][k]\) 表示到达 \((i,j)\) 可以包围的集合为 \(k\) 的最短路径。因为状态存在环,跑一个SPFA就可以了。最后权值和减最短路径就是价值。

如何判断一个点最终是否会被围起来?射线法! 从一点向随机方向引一条射线,如果射线和多边形的边相交奇数次,说明点在多边形内。否则在多边形外。考虑向右连射线,这里的相交需要左闭右开,也就是和一条竖直线段的非下端点相交才能算。

BZOJ1854-[SCOI2010]游戏 给定 \(n\) 个数对,每个数对中最多选择一个数字。要求选择 \(1,2,3,...\) 能达到的最大值。

巧妙的图论模型,并查集。 发现是一个二元关系,考虑建图。对于一个 \(size = p\) 连通块,如果不存在环,只能选择 \(p-1\) 个点,存在就能选择 \(p\) 个。显然,可以用并查集维护连通块是否存在环,以及连通块的最大值即可。

BZOJ1856-[SCOI2010]字符串 求存在多少长度为 \(n+m\) 的序列,存在 \(n\)\(1\)\(m\)\(0\) ,同时任意前缀的 \(1\) 多于 \(0\)

卡特兰数的扩展形式。终于知道卡特兰数怎么推了!!(如果早一点做这题,那场CF就不会血崩了QAQ)。

考虑这样一个 \(n \times m\) 的网格,设 \(1,0\) 分别为向右上/右下走。可转化为从 \((0,0)\) 到 $(n+m,n-m) $且不能碰到 \(y=1\) 的方案数。考虑容斥,总方案数为 \(C(n+m,n)\) 。将路线与 \(y=-1\) 第一个交点的左边沿着 $y=-1 $ 对称,发现不合法的方案为 \(C(n+m,m-1)\)

盗用 题解 的图。

img

BZOJ1857-[SCOI2010]传送带 在一个 \(2\) 维平面上有两条传送带,每一条传送带可以看成是一条线段。两条传送带分别为线段 \(AB\) 和线段 \(CD\)xx\(AB\) 上的移动速度为 \(P\) ,在 \(CD\) 上的移动速度为 \(Q\) ,在平面上的移动速度 \(R\) 。现在 xx 想从 \(A\) 点走到 \(D\) 点,他想知道最少需要走多长时间。

三分套三分。 时间 显然 是关于离开位置单峰的,三分一个 \(AB\) 再三分 \(CD\) 即可。证明嘛..

先挖坑。

JSOI

BZOJ1558-[JSOI2009]等差数列 给定 \(n\) 个数,\(m\) 个操作,每次给一段区间加一个等差数列,或者询问一段区间至少要用多少个等差数列来表示。\(n,m \leq 10^5\)

差分,线段树。 套路?考虑维护差分数组,修改操作变成了两个单点加和一个区间加,用线段树维护。

合并答案的时候比较复杂,用 \(s[0/1][0/1]\) 表示左右端点取不取的答案。一段长度为 \(n\) 等差数列就意味着,第一个是任意的差分值,剩下的 \(n-1\) 差分值相等。

BZOJ1822-[JSOI2010]Frozen Nova 冷冻波 套路的二分+最大流,需要判断线段是否与圆相交。

计算几何。 只是想记录一下判断线段 AB 是否与圆 C 相交的简便方法。一种情况:线段的一端在圆内/上,首先直接特判掉。 第二种情况:线段两端在圆的两边。

考虑与此线段平行的两条切线中间,被圆分开的两块区域。线段的端点只可能分别在两个区域。首先用叉积求出 \(2\triangle ABC\) ,然后除以长度得到与圆心的距离,大于 \(r\) 就判掉(防止卡精度,两边平方即可)。有可能线段的两端都在同一个区域,必定存在一个钝角。用点积判断。于是,代码:

    if (dist2(a, c) <= 1LL * r * r) return 1;
    if (dist2(b, c) <= 1LL * r * r) return 1;
    return dot(b, c, a) >= 0 && dot(a, c, b) >= 0
    && cross(c, a, b) * cross(c, a, b) <= 1LL * r * r * dist2(a, b);

NOI

BZOJ1564-[NOI2009]二叉查找树 给定一个Treap,总代价为深度\(\times\)距离之和。可以每次以 \(K\) 的代价修改权值(权值不能相同,并且是实数),问最小代价。

区间DP。 首先有一个很坑的点,权值不能相同。 事实上,由于权值是实数,所以随便改嘛...

然后数据范围也是假的。随便区间DP就好了..

POI

AUT 给定一个长度为 \(n\) 的置换。求存在多少个 \(n\) 阶竞赛图满足,如果边 \((u,v)\) 存在,那么 \((p(u),p(v))\) 存在。
\(n \leq 10000\)

简单计数。 考虑将置换分解为循环。容易发现:

对于同一置换:一个长度为 \(L\) 循环的方案数为 \(2^{L/2}\)

对于不同的置换:两个长度分别为 \(a,b\) 的循环,连接的方式有 \(2^{\gcd(a,b)}\)

TRO\(n+p+q\) 个箱子,如果填充第 \(i\) 个,那么第 \(\{i+p,i+p+q\}\ or\ \{i+q,i+p+q\}\) 个要被填充。请构造一种方式能够填满前 \(n\) 个箱子。
\(n \leq 100000\)

贪心。 不妨令 \(p < q\) ,从左到右扫描,如果没有放 \(i\)。如果可以,放 \(i+p\) 否则放 \(i+q\)

可是,为什么这样是满足条件的呢?显然,第 \(i+p+q\) 个箱子一定可以放。

不合法的情况当且仅当 \(i+p,i+q\) 都放不了。如果 \(i+q\) 放不了,那么一定是 \(i-p\) 经过了一次操作。而如果 \(i-p\) 进行操作,并且 \(i\) 也没放,一定会选择放 \(\{i-p, i, i+q\}\) 这与 \(i\) 没放矛盾。所以所有操作都能进行。

BZOJ1098-[POI2007]办公楼biu 给定一个图,求它反图的连通块数目。

链表优化BFS。 显然边数过多,不能把所有边建出来。考虑链表优化。

每一次取出链表的第一个元素,加入队列然后扩展。把与其相连的点标记,再遍历链表,如果遇到没有被标记的,就加入队列。由于每个元素最多被删除一次,每条边最多阻止扩展2次,所以总的复杂度就是 \(O(n+m)\)

BZOJ1100-[POI2007]对称轴osi 给定一个无自交的多边形,求对称轴数量。

计算几何,KMP。 显然,对称轴只可能是两个点的连线,或者是两条线段的连线。

如何保证轴两边对称?首先要确保距离相等,其次是角度(防止卡精度,又由于边长相等,可以用点积代替)。

然后这样就可以转化为串s,可以倍长得到ss后,用反串t来匹配、完整的匹配次数即是答案。

BZOJ1109-[POI2007]堆积木Klo 给定一个长度为 \(n\) 的序列 \(ai\),可以删除若干个数。求重新标号之后,\(a_i=i\) 的个数最大值。

三维偏序。 考虑能够满足 \(a_i=i\) 数的要求。

首先我们要保证 \(i, a[i]\) 是递增的,还有保证 \(i-a[i]\) 也是不减的(选择的相邻两个的差不应该超过位置差)。发现这是一个三维偏序。

这是一个假的三维偏序。因为 \(a[i]\) 递增, \(i-a[i]\) 不减时,\(i\) 一定递增。二维偏序用 \(LIS\) 即可。

BZOJ1122-[POI2008]账本BBB 给定一个由 \(+1\)\(-1\) 构成的长度为 \(n\) 的序列,提供两种操作:

  1. 将某一位取反,花销为 \(x\)
  2. 将最后一位移动到前一位,花销为 \(y\)

要求最终 \(p+sum_n=q\),且 \(p+sum_i≥0(1≤i≤n)\),求最小花销。

前缀和,贪心。 考虑预处理前缀,后缀最大前缀和 \(pre/suf\),枚举起始位置。 一个很巧妙的思想就是可以根据 \(pre,suf\) 求出移动后的前缀和最小值。然后我们贪心地把前面的若干个 \(-\) 改成 \(+\) 即可。代码就很短了:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000009;
int sum[maxn], pre[maxn], suf[maxn];
int n, p, q, x, y, mn, cnt; char s[maxn];
long long res = 1e18, tmp;
int main() {
    scanf("%d%d%d%d%d", &n, &p, &q, &x, &y);
    scanf("%s", s+1); sum[0] = p;
    for (int i=1; i<=n; i++)
        sum[i] = sum[i-1] + (s[i] == '+' ? 1 : -1);
    suf[n+1] = suf[n] = sum[n]; pre[1] = sum[1];
    for (int i=2; i<=n; i++) pre[i] = min(pre[i-1], sum[i]);
    for (int i=n-1; i>=1; i--) suf[i] = min(suf[i+1], sum[i]);
    for (int i=2; i<=n+1; i++) {
        tmp = 1LL * (n + 1 - i) * y;
        mn = min(suf[i] - sum[i-1], pre[i-1] + sum[n] - sum[i-1]);
        if (mn < 0) { cnt = (1 - mn) / 2; tmp += 1LL * cnt * x; } else cnt = 0;
        tmp += 1LL * abs(sum[n] + cnt * 2 - q) / 2 * x;
        res = min(res, tmp);
    }
    printf("%lld\n", res);
    return 0;
}

COCI

BZOJ1182-[COCI2009]PLAHTE 在平面坐标系中,有 \(n\) 个矩形。时刻0,在原点处有一个xx,之后的每一时刻,每个xx会向周围八个方向扩展。给定 \(Q\) 个询问,要求回答时刻 \(t\) ,所有矩形覆盖的xx总数是多少。注意,没有矩形覆盖原点。
\(n, Q \leq 10 ^ 6\)

二阶导数(大雾) 二阶差分,加速度。 很显然需要搞一个前缀和。

矩形总共有两种放置情况,一种是在一个象限,还有一种是横跨两个象限。为了 方便 处理,可以将后者拆分为两个矩形,同时,我们可以将矩形强制放在第一象限。还是为了方便我们可以用容斥原理,拆分为四个左下角固定,边长无穷大的矩形。

然后这个东西就很好弄了,扩散有一个初始速度,之后还有一个加速度。叠加起来就好了。本质上是一个二阶差分。

BZOJ1939-[COCI2010] Zuma 有一行 \(n\) 个弹子,每一个都有一个颜色。每次可以让超过 \(K\) 个的连续的同颜色的一段弹子消失,剩下的会重新紧凑在一起。你有无限的所有颜色的弹子,要求在这行弹子中插入最少的弹子,使得弹子全部消失。

真·祖玛,动态规划,奇妙的状态。 定义 \(f[l][r][x]\) 表示对于 \((l,r)\) 这个子序列,我们需要插入 \(x\)\(a[l]\) 才能使原序列消失,所需要的总的最小代价。 然后有三种转移:

直接在前面插入,\(f[l][r][x] = f[l][r][x+1] + 1\)

直接删除,\(f[l][r][K-1] = f[l+1][r][0]\)

和后面的匹配 ,\(f[l][r][x] = f[l+1][p-1][0] + f[p][r][x+1] (l+1 \leq p \leq r, a[l] = a[p])\)

Baltic

BZOJ1356-[Baltic2009]Rectangle 给出 \(n\) 个点,要你从这些点中找出四个点来组成一个矩形,面积最大。注意可以不与坐标轴平行。

几何基础。 考虑一个矩形,他的两条对角线长度相同,且中点相同。就可以 \(O(n^2)\) 计算出来,然后按照对角线长度和中点位置排序,考虑可以配对的所有对角线。因为能共圆的点不多,所以直接暴力枚举可过。复杂度是 \(O(n^4)\) 的。

事实上不用那么萎。考虑矩形的面积是对角线长度的平方,乘以夹角的 \(\sin\) 考虑按照极角序来排,然后用两个指针,始终维持夹角在小于等于 \(\frac {\pi} {2}\) 的最大值。酱复杂度就是 \(O(n^2 \log n)\) 啦,可能实际并没有快多少?

BZOJ1761-[Baltic2009]beetle 在一条直线上有 \(n\) 个点,每个点 \(m\) 升水. 一个虫子在坐标轴 \(0\) 上,它每个单位时间移动一格,每个点的水每单位时间消失 \(1\) 升。问虫子最多可以喝到多少水,喝水的时间忽略不计。

提前费用计算。 显然是提前费用计算的套路,由于不知道最终要哪些点的水,枚举喝的点的数量即可。然后就是套路区间DP啦。

APIO

BZOJ2303-[APIO2011]方格染色 有一个 \(n \times m\) 的方格,可以黑白染色,已经有某些格子已经染色。要求全部染色后,所有 \(2 \times 2\) 的区域中,恰好有奇数个黑块。问染色的方案数。

奇妙的并查集。\(S(i,j) = a[i][j] \oplus a[i-1][j] \oplus a[i][j-1] \oplus a[i-1][j-1]\) 考虑将 \(S(1..i-1, 1..j-1)\) 全部都 xor 起来。于是 \(a[1][1] \oplus a[i][1] \oplus a[1][j] \oplus a[i][j] = [i,j都是偶数]\) 。于是就有 \(2n-1\) 个自由变元(也是因为有 \(n^2\) 个变元, \((n-1)^2\) 个方程)。

然后我们可以枚举 \(a[1][1]\) 的值。这些方程就可以的到 \(a[i][1]\)\(a[1][j]\) 的取值是否相同。用一个带权并查集搞一搞,判矛盾以及统计连通块数目。

Code:

int getpa(int x) {
    if (pa[x] == x) return x;
    int p = getpa(pa[x]);
    g[x] ^= g[pa[x]];
    return pa[x] = p;
}
bool link(int x, int y, int z) {
    int u = getpa(x), v = getpa(y);
    if (u == v) return !(g[x] ^ g[y] ^ z);
    g[v] = (g[x] ^ g[y] ^ z); pa[v] = u;
    return 1;
}
int calc() {
    rep (i, 1, n+m) { pa[i] = i; g[i] = 0; }
    pa[n+1] = 1;
    rep (i, 1, Q)
        if (!link(x[i], y[i]+n, z[i])) return 0;
    int res = 0;
    rep (i, 1, n+m) if (pa[i] == i) res++;
    return Power(2, res-1);
}
int main() {
    n = read(); m = read(); Q = read();
    flag[1] = flag[0] = 1;
    rep (i, 1, Q) {
        x[i] = read(); y[i] = read(); z[i] = read();
        if (x[i] + y[i] == 2) { flag[z[i]] = 0; i--; Q--; continue; }
        z[i] ^= (x[i]^1)&(y[i]^1)&1;
    }
    if (flag[1]) ans = (ans + calc()) % MOD;
    if (flag[0]) {
        rep (i, 1, Q) if (x[i] > 1 && y[i] > 1) z[i] ^= 1;
        ans = (ans + calc()) % MOD;
    }
    printf("%d\n", ans);
    return 0;
}

BZOJ1178-[APIO2009]CONVENTION会议中心 给定若干条线段。要求选取最多的线段,使之不重叠。在最多的基础上,要求选取的线段字典序最小。

倍增,贪心。 如果没有字典序最小这个条件,就是一个裸的贪心。然后这就有一个倍增的经典应用。

考虑首先搞掉所有包含其他线段的线段。然后倍增,\(f[i][j]\) 表示第 \(i\) 条线段之后,所选择 \(2^j\) 条线段的最右边一条。只需要贪心预处理 \(f[i][0]\)

然后仍是贪心地考虑放入线段 \(1,2,...\) ,如果放入线段之后,对答案不会产生影响,就选择这条线段。

BZOJ1912-[APIO2010]patrol巡逻 给定一棵有根树,可以加入 \(K = 1\ or\ 2\) 条边,使得从 \(1\) 出发,遍历所有的边,问需要走的最小距离。

DFS,贪心。 对于 \(K=1\) 的情况,显然是选取直径的两端,可以减小直径-1次移动。

考虑 \(K=2\) ,假设已经增加了一条边。对于一条边,如果它之前已经被覆盖了,第二次仍然被覆盖,那么这条边就要经过 2 次(因为新加入的两条边一定要走,这条边要走两次才能回到原来的一侧)。然后再次求最长链即可。

BZOJ1913-[APIO2010]signaling信号覆盖 给定 \(n\) 个平面上的点,任意三点不共线,任意四点不共圆,问随机选择三个点构成的外接圆,能够覆盖的点数的期望值。

\(n \leq 1500\)

计算几何。 注意到对答案的贡献涉及到四个点的关系。所以考虑任意四个点 \(A,B,C,D\) ,分成两种情况。构成了一个凹四边形,那么对答案的贡献为 \(1\) ;构成一个凸四边形,对答案的贡献为 \(2\) (这个很容易通过四点共圆得到),显然这样的统计不会重复。

于是就变成统计凹/凸多边形的个数。一个简单的方法就是,枚举凹多边形的凹点 \(C\) ,极角排序。然后对于另一端 \(D\) ,统计 \(B,C\) 的对数。

USACO

BZOJ4757 给定长度为 \(N\) 的序列 \(ai\) ,对每个 \(ai\) 分配 \(ci\) ( \(ci\) 为正整数),且 \(ci\) 之和等于 \(K\) ,求出最小的 \(ai/ci\) 之和。

奇妙的贪心。 考虑这样一个事实,对于 \(ai,aj\)\(ci\) 增大 \(1\) 的同时, \(cj\) 减小 \(1\) 。那么偏移量一定是一大一小,而且增大的速度更快。根据贪心玄学的思想,我们二分偏移量,使得这个偏移量最小,那么答案一定是最优的。

BZOJ4768 给定一个长度为 \(N\) 的序列,允许翻转一个非连续子序列,求最长不下降子序列长度。所有数 \(<=50\)

奇妙的DP,附加状态。\(f[i][j][l][r]\) 表示 \([i,j]\) 内左边是 \(>=l\) ,右边是 \(<=r\) 的最大不下降子序列长度。然后两端的数换不换都转移一下。

为什么要这样考虑呢?因为翻转相当于 \([i,j]\) 两边的交换。为了保证是递增的, \([l][r]\) 显然是需要的。

BZOJ4741\(n\) 个点,保证不存在三点共线。对于所有构成的三角形,内部的点数就是它的价值。对于每一价值,计算三角形的块数。
\(n \leq 300\)

奇妙的容斥原理,计算几何。 似乎很不可做的样子,事实上,利用容斥原理,我们就可以做到 \(O(1)\) 计算一个三角形内的点数。考虑预处理:

对于所有二元组 \((u,v)\) ,计算满足向量 \((u,v) \rightarrow (u,p)\) 的角不大于 \(\pi\) 的点 \(p\) 数论。

对于所有三元组 \((u,v,w)\) ,计算满足 \(p\) 在向量 \((u,v) \rightarrow (u,w)\) 之间的个数。

之后配一波系数,容斥一下就好了。

BZOJ4586 给定一个长度为 \(n\) 的初始序列 \(Ai\) ,和一个目标序列 \(Bi\) 。可以花费 \(x\) 的代价使得一个数增加 \(1\) ,或者花费 \(y\) 的代价使得一个数减少 \(1\) 。对于 \((i,j)\) 花费 \(|i-j|z\) 的代价使得 \(Ai\) 减一, \(Aj\) 加一。问最小需要的代价总和。
\(n \leq 100000\)

奇妙的贪心,反悔操作。 考虑经典的堆来维护返回操作。维护两个堆,分别表示多余的和缺少的。对于每个树默认操作是直接增加或减少,同时把这个反悔后的新代价放入堆。

BZOJ4410 给定一个网格图,拆除所有行的竖向边,拆出所有列的横向边的代价都已经给定。问使得所有方格连通的最小代价。

堆,Kruskal。 考虑Kruskal算法的思想,我们要求最小生成树,就需要按照边权从小到大选择。拆出未连接的边即可。注意一个方向的所有边都拆完不一定连通。

BZOJ4411 给定 \(n\) 个点,请你确定一条横线和一条竖线,使得平面划分成的四个区域内点最大值最小.

线段树上二分。 枚举直线 \(x=a\) ,显然两边在线段树中可以动态插入/删除。二分 \(y=b\) 的位置。注意到两端是一个下凸函数,取 max 之后仍然下凸。这个东西就可以线段树二分辣。

BZOJ4409 有一个 \(N\) 个点的环,最终点 \(i\) 存在 \(ri\) 头牛。有 \(\sum ri\)头牛。可以选择最多 \(k\) 个点,然后牛分配在这k个点里。之后每一头牛可以不动,也可以顺时针走 \(d\) 格并呆在那里,要耗费d的能量。通过合理分配。使得消耗的总能量最小。
\(n \leq 1000\)

斜率优化DP。 由于存在环,显然我们可以枚举第一个选择点的位置 \(i\) 。用 \(f[j][k]\) 表示从 \(i\) 出发,选择 \(k\) 个点,到 \(j\) 的最优解。然后这个东西是可以斜率优化的。

同时有注意到切线是递增的,所以可以做到 \(O(n^2k)\) 。然鹅这个东西是要用 \(k-1\) 层去更新第 \(k\) 层。所以需要把 \(k\) 放在外面。

随便一写就rk3了??

BZOJ3939 有一个 \(n \times m\) 的网格,每个格子有一个取值。从 \((1,1)\) 出发,每一次可以跳到严格右下方的格子。问存在多少中方案能够跳到 \((n,m)\)
\(n, m \leq 750\)

动态规划,动态开点线段树。 显然DP+容斥嘛。维护一个二维前缀和,减去与当前颜色相同的方案数。

考虑对每个颜色开一个动态开点线段树。每一次最多增加 \(O(\log n)\) 个节点。所以总共的空间是 \(O(n^2 \log n)\) 的。

BZOJ4509 数轴上有 \(n\) 个点。可以在任意一个位置上引爆,使得爆炸范围内的点再次引爆,但是引爆半径减一。问最小的初始引爆半径,使得所有点都被引爆。

动态规划,单调性。 注意到左右两部分是无关的,所以我们可以求出前缀和后缀所需要的最小引爆半径。

\(f[i]\) 表示 \(i\) 左边都被引爆的最小半径。于是有:

\(f[i] = \min \{ ai - aj, f[j+1] + 1 | j < i, a[i] - a[j] > f[j] \}\)

注意到前半部分是随着 \(j\) 的变大而单调递减的,而后面是单调递增的。所以我们可以求出两个接近时候的最值。复杂度 \(O(n)\)

BZOJ4099 数轴上有 \(n\) 个点,每个点有一个大小 \(si\)。从任意一个位置出发,沿着同一方向跑至少 \(si\),就可以突破点 \(i\),得到更大的空间。问无法逃脱的线段总长度。

set 考虑按照点的大小,从大到小插入。当插入一个点后,查找它的前驱和后继,如果无法突破,就打上标记。由于每个点至多被标记一次,所以复杂度是对的。

BZOJ3429 有一个 \(n \times m\) 的01矩阵,以及他的目标状态。每一次可以修改 \(b*b\) 大小的子矩阵,使得原来的矩阵到达目标状态。问最大的 \(b\)是多少。

模拟? 显然,这个 \(b\) 是满足单调性的。当大的 \(b\) 满足时,小的 \(b\) 也满足。所以考虑暴力修改,当无法再次修改时,用更小的 \(b\) 替换。

Other

BZOJ1426-收集邮票 \(n\) 种邮票,你要收集所有种类的邮票。每次能买一张,买到的邮票是哪一种,概率均为 \(1/n\) 。购买第 \(k\) 张邮票需要 \(k\) 元钱。现在手中没有邮票,问得到所有种类的邮票需要钱的期望。

期望神题,平方相关。 先挖坑吧。。智商还不够

题解一

题解二

BZOJ2708-[Violet 1]木偶 给定两个序列 \(\{a_i\},\{b_i\}\) ,满足 \(a_i = b_i\) 。重复一下步骤,选择任两个 \(i,j\) 满足 \(|a_i-b_j| \leq 1\) ,将个两个数配对,直到无法再配对。问最坏情况下,未配对的 \(a_i\) 最多有多少个。

贪心,动态规划。 显然可以从小到大排序。这就是一个二分图,一个显然的性质:边不会出现交叉。因为只能连接差的绝对值不超过 \(1\) 的数,所以交叉只会是相邻的两个,显然这个可以用连向自己的来代替,并且答案不会更劣。

然后就可以推出结论,配对一定是形如 //..//\\..\\//... 这样延伸下去的。然后就可以DP了。需要搞一个 \(calc(i,j)\) 表示 \([i,j]\) 无法配对的最多有多少。考虑从大到小枚举,判断 应该/不应该 配对的两个数能否配对。

猜你喜欢

转载自www.cnblogs.com/cyanic/p/9159481.html
今日推荐