AtCoder瞎作第二弹

ARC 067

F - Yakiniku Restaurants

题意

\(n\) 家饭店,\(m\) 张餐票,第 \(i\) 家和第 \(i+1\) 家饭店之间的距离是 \(A_i\) ,在第 \(i\) 家饭店用掉第 \(j\) 张餐票会得到 \(B_{i, j}\) 的好感度,能够从任意一个饭店出发,求好感度减通过的距离和的差的最大值。c++

\(2 \le n \le 5000, 1 \le m \le 200, 1 \le A_{i, j}, B_{i, j} \le 10^9\)git

题解

作题千万条,看题第一条。app

显然咱们不会走回头路,那么每次咱们选择的必定是一段连续区间 \([l, r]\)函数

考虑每一个 \(B_{i, j}\) 对于哪些区间有贡献,找到左右第一个比它的 \(B_{x, j}, B_{y, j} \ge B_{i, j}\) ,那么它贡献的区间其实就是 \(l \in (x, i], r \in [i, y)\)优化

咱们利用单调栈算出端点,而后矩形加利用二维差分实现便可。spa

\(\mathcal O(n^2 + nm)\)debug

代码

Submission #5013272rest

ARC 068

F - Solitaire

题意

有一个双端队列。code

首先将 \(n\) 个数 \(1\sim n\) 从小到大任意先后地添入队列。而后任意先后地弹出队列,求最后弹出来的排列中,第 \(k\) 个数为 \(1\) 的排列有多少种。

\(1 \le k \le n \le 2000\)

题解

一开始添完的序列性质显然是将 \(1\) 分红两段,左边递减,右边递增。

因为构造合法序列是左右弹元素,那么性质就比较好推了。

  • \(k\) 个数为 \(1\)
  • \(k - 1\) 个数可拆分为至多两个降低子序列;
  • \(k - 1\) 个数的最小值必定大于后 \(n - k\) 个数的最大值。

先考虑最后 \(n - k\) 个数的方案,若是咱们肯定了前 \(k\) 个数,那么剩下的 \(n - k\) 个数是由一个单调队列弹出来的,除了最后一次只能弹一个,别的每次都有两种选择,因此方案是 \(2^{\max(0, n - k - 1)}\)

而后前面拆分红至多两个降低子序列,这个和 这道题 是同样的。

咱们如今只须要知足第一个限制了,因为第 \(k\) 个数是须要最小值,用至多选 \(k\) 个和 \(k - 1\) 个差分一下便可。

而后利用以前那个题的组合数结论就能够作到 \(\mathcal O(n)\) 了。

其实那个组合数有个更优美的形式,也就是 \(\displaystyle {n + m \choose m} - {n + m \choose m - 1}\) ,意义明显许多。

代码

Submission #5016238

ARC 070

E - Narrow Rectangles

题意

\(n\) 个高为 \(1\) 的矩形,第 \(i\) 个矩形 \(y\) 轴范围为 \([i - 1, i]\)\(x\) 轴范围为 \([l_i, r_i]\)

须要横向移动一些矩形,使得全部矩形是连通的(角也算),对于一个矩形,横向移动 \(x\) 距离的代价为 \(x\) ,求出最小代价。

\(1 \le n \le 10^5, 1 \le l \le r \le 10^9\)

题解

首先考虑暴力 \(dp\) 即令 \(f_{i, j}\) 为第 \(i\) 层矩形右端点为 \(j\) ,前 \(i\) 层已经联通所须要的最小代价。

\(len_i = r_i - l_i\) ,每次只须要两条线段相交便可。转移十分显然:
\[ \begin{aligned} f_{i, p} &= |r_i - p| + \min_{p - len_i \le j, p \ge j - len_{i - 1}} f_{i - 1, j} \\ &= |r_i - p| + \min_{j - len_{i - 1} \le p \le j + len_i} f_{i - 1, j} \end{aligned} \]

咱们能够把 \(f_i\) 看作一个关于 \(p\) 的函数,设 \(g_i(p) = |r_i - p|\) ,那么形式为:
\[ f_i(p) = g_i(p) + \min_{j - len_{i - 1} \le p \le j + len_i} f_{i - 1}(j) \]

不难观察到函数这个图像实际上是一个分段一次函数,且斜率从 \(-(i - 1), - (i - 2), \cdots, 0, \cdots, i - 2, i - 1\) 递增(拆开重合点)。(不难利用概括证实)其实也是一个凹函数,最小值在 \(k = 0\) 处取得。

那么考虑后面那个 \(\min\) 等价于把 \(k_x < 0\) 的部分向左平移 \(len_{i - 1}\) (由于咱们想尽可能向右取),\(k_x > 0\) 的部分向右平移 \(len_i\) ,而后最后全局加上 \(g_i\) 就好了。

咱们其实能够用 \(Splay\) 维护这个凸壳,每次只须要支持区间加一次函数,全局平移便可。

但显然能够更方便地解决,因为最后咱们只须要求 \(k = 0\) 时候的函数值,咱们利用对顶堆维护 \(k < 0, k > 0\) 的位置,每次讨论一下插入的绝对值函数的 \(0\) 点位置便可。

讨论的时候能够顺便计算一下当前的答案。

总结

对于加绝对值函数,而且取 \(\min, \max\)\(dp\) 均可以考虑是否存在凸壳,而后经过 线段树/ \(Splay\) / 对顶堆 维护这个 \(dp\) 值便可。

代码

#include <bits/stdc++.h>

#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl

using namespace std;

typedef long long ll;

template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }

inline int read() {
    int x(0), sgn(1); char ch(getchar());
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
    for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    return x * sgn;
}

void File() {
#ifdef zjp_shadow
    freopen ("E.in", "r", stdin);
    freopen ("E.out", "w", stdout);
#endif
}

const int N = 1e5 + 1e3;

int n, l[N], r[N], len[N];

ll tl, tr, ans;

priority_queue<ll> L;
priority_queue<ll, vector<ll>, greater<ll>> R;

int main () {

    File();

    For (i, 1, n = read()) 
        l[i] = read(), r[i] = read(), len[i] = r[i] - l[i];

    L.push(r[1]); R.push(r[1]);
    For (i, 2, n) {
        tl -= len[i - 1]; tr += len[i];
        ll lp = L.top() + tl, rp = R.top() + tr;
        if (lp <= r[i] && r[i] <= rp) 
            L.push(r[i] - tl), R.push(r[i] - tr);
        else if (r[i] >= rp) {
            ans += r[i] - rp; R.pop(); L.push(rp - tl);
            R.push(r[i] - tr); R.push(r[i] - tr);
        } else {
            ans += lp - r[i]; L.pop(); R.push(lp - tr);
            L.push(r[i] - tl); L.push(r[i] - tl);
        }
    }
    printf ("%lld\n", ans);

    return 0;

}

F - HonestOrUnkind

题意

\(n = a + b\) 我的,其中有 \(a\) 我的是诚实的,\(b\) 我的是不友好的。每次你能够问 \(x\)\(y\) 是否是一个诚实的人。若是 \(x\) 是诚实的,那么必定会回答真正的答案。不然,他会随便告诉你一个结果。(交互库有必定策略地回答。)

如今告诉你 \(a, b\) ,你须要肯定是否必定能问出来。若是问不出来输出 Impossible 。若是能问出来,须要在 \(2n\) 步内问出来。

题解

首先咱们考虑何时是 Impossible ,显然当 \(b \ge a\) 的时候,\(b\) 能够很好的隐藏在 \(a\) 中。由于问任意一我的,\(b\) 均可以根据 \(a\) 的决策,来颠倒黑白。只有当 \(a\) 超过 \(n\) 的一半的时候,咱们问任意一我的均可以根据 \(\text{Y, N}\) 中较多的那项肯定类别。

接下来,咱们不难想到一个乱搞。就是一开始随机问几我的,而后问全部人他的类别,就能够肯定类别了。若是是老实人,而后就能够一遍问它就能获得全部人的类别了。咱们打乱一下询问顺序,那么这样指望下是 \(3n\) 的。

咱们其实能够继续优化一下乱搞,加上以下几个优化:

  • 若是问出当前人的类别,以前回答类别不一样的人,确定不是老实人,以后咱们全都跳过不问便可。
  • 若是咱们当前问的 \(\text{Y, N}\) 其中较多的那个个数,大于剩下没有肯定的不友好的人数,就能够肯定这我的的类别了。
  • 若是当前只剩下友好/不友好,咱们就能够直接不问,而后肯定便可。

指望应该是 \(1.5n\) 的?而后全都加上。。就能够过啦qwq(我也是交了十几发才艹过的。。)


显然有肯定性作法,咱们须要基于这样一个结论,若是 \(x\)\(y\) 是不友好的,那么 \(x, y\) 确定不可能同时是诚实的,若是咱们忽略他们,剩下的老实人个数仍然大于一半。

咱们用个栈,把每一个人放进去,若是栈顶这我的说当前这我的是不友好的,咱们把栈顶弹出,而后忽略他们。

而后最后剩下了 \(a_0, \cdots, a_{k - 1}\) 其中每一个 \(a_i\) 都说 \(a_{i + 1}\) 是诚实的,那么显然 \(a_{k - 1}\) 必定是诚实的。为何呢?由于其中必定有我的是老实人,那么在它后面的全部人必定都是老实人,那么最后一我的必是老实人。

而后咱们就能够在稳定的 \(2n\) 次里问出全部人的类别啦。(好妙啊~)

代码

放个瞎JB乱搞

ARC 072

F - Dam

题意

有一个容量为 \(L\) 的水库,天天晚上能够听任意体积的水。天天早上会有必定温度和体积的水流入水库,且要保证流入水以后水的整体积不能超过 \(L\) 。令体积分别为 \(V_1,V_2\) ,温度分别为 \(t_1,t_2\) 的水混合后的温度为 \(\displaystyle \frac {V_1 * t_1 + V_2 * t_2} {V_1 + V_2}\) 。初始水库为空。现给出 \(n\) 天流入水的体积和温度,分别最大化每一天中午水库满容量时的水温。

\(1 \le n \le 5 \times 10^5\)

题解

一道颇有意思的题~

咱们能够发现两个性质:

  1. 当前水温小于之前水温时必然会拉低总水温,因此必定会和前面的水混合,直接向前不断合并便可。
  2. 当前水温大于前面时,直接将前面舍弃能够获得更高的温度,但要求总量必须为 \(L\) ,这样有可能出现不够加满水坝的状况,所以还要保留一段。

咱们利用一个单调队列(队列中的元素队首到队尾按 \(t\) 单调递增),每次当前体积 \(>L\) 咱们不断弹掉队首,使得体积变小。而后队尾温度比队尾前一个低,咱们就合并,直至不能合并便可。

至于为何是对的呢?你能够考虑把每一个水看作一个向量,咱们至关于看向量和的斜率,咱们其实就是须要贪心地维护一个下凸壳,本质上是同样的。

代码

Submission #5206287

ARC 073

E - Ball Coloring

题意

\(n\) 个盒子,每一个盒子里面有两个球,分别写了一个数字 \(x_i, y_i\) 。如今须要把每一个盒子其中的一个球染成红色,另一个染成蓝色。

\(R_{\max}\) 为红球数字最大值,其余的同理,求 \((R_{\max} - R_{\min})(B_{\max} - B_{\min})\) 的最小值。

\(n \le 2 \times 10^5\)

题解

脑子是个好东西,我也想要一个QAQ

令全局 \(x_i, y_i\) 最大值为 \(A_{\max}\) ,最小值为 \(A_{\min}\) 。显然 \(R_{\max}, B_\max\) 其中一个须要取到 \(A_\max\)\(\min\) 同理。

咱们考虑分两种状况讨论。

  • 最大值和最小值被两边分别取到了。

    不妨令 \(R_\max = A_\max, B_\min = A_\min\) ,那么咱们显然须要最小化 \(B_\max\) ,最大化 \(R_\min\)

    那么显然对于每一个盒子,咱们把较小的那个给 \(B\) ,较大的给 \(R\) ,显然是最优的。

  • 最大值和最小值都给一边取到了。

    不妨令 \(R_\max = A_\max, R_\min = A_\min\) ,那么咱们就须要最小化 \(B_\max - B_\min\)

    咱们考虑从小到大枚举 \(B_\min\) ,而后动态维护 \(B_\max\) 的最小值。

    若是当且 \(B_\min = A_\min\) ,咱们显然 \(B_\max\) 取到全部的 \(\min\{x_i, y_i\}\) 的最大值是最优的。

    而后咱们每次把 \(B_\min\) 变大,也就是翻转 \(B_\min\) 的颜色,随便维护一下最值便可。

\(\mathcal O(n \log n)\)

代码

Submission #5212035

F - Many Moves

题意

一个长为 \(n\) 的数轴,一开始上面有两个盒子在 \(A, B\) ,有 \(q\) 次要求,每次给出一个坐标 \(x_i\) ,须要把其中一个盒子移到 \(x_i\) ,问最少移动的距离和。

\(1 \le n, q \le 2 \times 10^5\)

题解

惟一一道本身作出来的 \(F\) TAT 虽然很水。

假设当前处理第 \(p\) 个要求。考虑 \(dp\) ,设 \(f_{i, j}\) 为当前两个盒子分别在 \(i, j\) 的最少距离和,转移显然。

但显然每次咱们有个盒子的位置必定在 \(x_{p - 1}\) ,咱们只须要记 \(f_i\) 为其中一个盒子在 \(x_{p - 1}\) ,另一个在 \(i\) 的最少距离和。

显然一次咱们不会同时移动两个盒子,这样必定不优。

  • \(i \not = x_{p - 1}\) ,显然不动 \(i\) ,动 \(x_{p - 1}\) 便可,因此有 \(f'_i = f_i + |x_p - x_{p - 1}|\)
  • 对于 \(i = x_{p - 1}\) 咱们考虑枚举上次的另一个位置 \(j\) ,那么有 \(f_{x_{p - 1}} = \min_{j} \{f_j + |x_p - j|\}\)

直接实现是 \(\mathcal O(n^2)\) 的,对于第一个转移就是全局加,对于第二个转移拆绝对值,而后维护 \(f_i \pm i\) 的最小值便可。

均可以用线段树实现 \(\mathcal O(n \log n)\)

代码

Submission 5230528

ARC 075

F - Mirrored

题意

定义 $rev(n) $ 为将 \(n\) 的十进制位翻转的结果,例如 \(rev(123) = 321, rev(4000) = 4\)

给定正整数 \(D\) ,求有多少个 \(N\) 知足 \(rev(N) = N + D\)

\(1 \le D < 10^9\)

题解

考虑固定长度为 \(L + 1\) ,假设从低到高每一位分别是 \(b_i\) ,那么其实就是
\[ \begin{aligned} rev(N ) - N &= \sum_{i = 0}^{L} (10^{L - i} - 10^i) b_i\\ &= \sum_{i = 0}^{\lfloor \frac L2\rfloor} (10^{L - i} - 10^i) (b_i - b_{L - i}) \end{aligned} \]

咱们等价求把 \(0 \sim \lfloor \frac L2\rfloor\) 的每一个 \(v_i = 10^{L - i} - 10^i\) 乘上 \(-9 \sim 9\) 和为 \(D\) 的方案数。(对于每一个 \(-9 \sim 9\) 对应了两个数的一种组合)。

直接 \(dfs\) 可能会 TLE ,考虑利用性质优化,咱们观察到:
\[ v_i > \sum_{j = i + 1}^{\lfloor \frac L2\rfloor} 9 v_j \]

那么意味着若是咱们肯定前 \(i\) 位,组合出来的数与 \(D\) 相差 \(v_i\) 时,显然是之后不管如何也没法恢复的。

那么每一步其实咱们的填法从 \(18\) 下降到只有 \(2\) 种了。

咱们须要枚举的长度应该是 \(L_D \sim 2L_D\)\(L_D\)\(D\) 十进制的长度),由于咱们加减的范围应该是恰好 \(L_D\) 的,超过 \(2L_D\) 加减最小数已经超过了 \(D\) 显然没法获得。

有科学的复杂度为
\[ \begin{aligned} \mathcal O(\sum_{i = L_D}^{2L_D} 2^{\lfloor \frac i 2\rfloor}) &= \mathcal O(2^{L_D}) = \mathcal O(2^{\log_{10} D})\\ &= \mathcal O(2^{\frac{\log_2 D}{\log_2{10}}}) = \mathcal O(D^{\log_{10} 2}) \approx \mathcal O(D^{0.3010}) \end{aligned} \]

跑的挺快的。

代码

#include <bits/stdc++.h>

#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl

using namespace std;

typedef long long ll;

template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }

inline int read() {
    int x(0), sgn(1); char ch(getchar());
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
    for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    return x * sgn;
}

void File() {
#ifdef zjp_shadow
    freopen ("F.in", "r", stdin);
    freopen ("F.out", "w", stdout);
#endif
}

int D, len, up;

ll Pow[20], v[20];

int Pool[20], *d = Pool + 10;

ll Dfs(ll cur, int dep) {
    if (dep == up) return !cur;
    int t = cur / v[dep]; ll res = 0;
    For (i, t - 1, t + 1) if (abs(i) <= 9 && abs(cur - i * v[dep]) < v[dep])
        res += (d[i] - (i >= 0 && !dep)) * Dfs(cur - i * v[dep], dep + 1);
    return res;
}

int main () {

    File();

    for (int tmp = (D = read()); tmp; tmp /= 10) ++ len;

    Pow[0] = 1;
    For (i, 1, 18) 
        Pow[i] = 10ll * Pow[i - 1];

    Rep (i, 10) Rep (j, 10) ++ d[i - j];

    ll ans = 0;
    For (i, len, len << 1) {
        For (j, 0, up = i >> 1)
            v[j] = Pow[i - j - 1] - Pow[j];
        ans += (i & 1 ? d[0] : 1) * Dfs(D, 0);
    }
    printf ("%lld\n", ans);

    return 0;

}

ARC 079

F - Namori Grundy

### 题意

给你一个 \(n\) 个点的有向环套树,须要对于每一个点定取值 \(a_i \ge 0\) ,知足。

  • 对于全部边 \((i, j)\)\(a_i \not = a_j\)
  • 对于 \(0 \le x < a_i\) 都存在至少一条边 \((i, j)\) 使得 \(a_j = x\)

问是否存在一种合法方案。

\(2 \le n \le 2 \times 10^5\)

题解

其实操做本质上其实就是个 \(\mathrm{mex}\) ,对于树上的 \(\mathrm{mex}\) ,每一个节点的值应该是肯定的。

须要考虑环,处理完全部环上节点的子树,就得出了每一个环上节点的取值下界 \(b_i\)

  1. 全部 \(b_i\) 相同且环长度为偶数:咱们隔一个数就把当前数加 \(1\) 便可。

  2. 全部 \(b_i\) 相同且环长度为奇数:那么隔一加一的就不行了,其实不难发现这样没法构造出合法方案。

  3. 存在有 \(b_i\) 不一样:找到 \(b_i\) 最小的点 \(v\) 把它 \(+1\) ,而后依次考虑 \(u \to v\) 而后若是 \(b_u = b_v + 1\) 那么咱们继续操做便可,不难发现这样操做必定会在某点中止不会绕圈。

这样咱们就在 \(\mathcal O(n)\) 的时间内判断出来了。

代码

Submissions 5475398

相关文章
相关标签/搜索