AtCoder瞎做第二弹

ARC 067

F - Yakiniku Restaurants

题意

n 家饭店,m 张餐票,第 i 家和第 i+1 家饭店之间的距离是 Ai ,在第 i 家饭店用掉第 j 张餐票会获得 Bi,j 的好感度,可以从任意一个饭店出发,求好感度减经过的距离和的差的最大值。

2n5000,1m200,1Ai,j,Bi,j109

题解

做题千万条,看题第一条。

显然我们不会走回头路,那么每次我们选择的一定是一段连续区间 [l,r]

考虑每个 Bi,j 对于哪些区间有贡献,找到左右第一个比它的 Bx,j,By,jBi,j ,那么它贡献的区间其实就是 l(x,i],r[i,y)

我们利用单调栈算出端点,然后矩形加利用二维差分实现即可。

O(n2+nm)

代码

Submission #5013272

ARC 068

F - Solitaire

题意

有一个双端队列。

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

1kn2000

题解

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

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

  • k 个数为 1
  • k1 个数可拆分为至多两个下降子序列;
  • k1 个数的最小值一定大于后 nk 个数的最大值。

先考虑最后 nk 个数的方案,如果我们确定了前 k 个数,那么剩下的 nk 个数是由一个单调队列弹出来的,除了最后一次只能弹一个,别的每次都有两种选择,所以方案是 2max(0,nk1)

然后前面拆分成至多两个下降子序列,这个和 这道题 是一样的。

我们现在只需要满足第一个限制了,由于第 k 个数是需要最小值,用至多选 k 个和 k1 个差分一下即可。

然后利用之前那个题的组合数结论就可以做到 O(n) 了。

其实那个组合数有个更优美的形式,也就是 (n+mm)(n+mm1) ,意义明显许多。

代码

Submission #5016238

ARC 070

E - Narrow Rectangles

题意

n 个高为 1 的矩形,第 i 个矩形 y 轴范围为 [i1,i]x 轴范围为 [li,ri]

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

1n105,1lr109

题解

首先考虑暴力 dp 即令 fi,j 为第 i 层矩形右端点为 j ,前 i 层已经联通所需要的最小代价。

leni=rili ,每次只需要两条线段相交即可。转移十分显然:

fi,p=|rip|+minplenij,pjleni1fi1,j=|rip|+minjleni1pj+lenifi1,j

我们可以把 fi 看做一个关于 p 的函数,设 gi(p)=|rip| ,那么形式为:

fi(p)=gi(p)+minjleni1pj+lenifi1(j)

不难观察到函数这个图像其实是一个分段一次函数,且斜率从 (i1),(i2),,0,,i2,i1 递增(拆开重合点)。(不难利用归纳证明)其实也是一个凹函数,最小值在 k=0 处取得。

那么考虑后面那个 min 等价于把 kx<0 的部分向左平移 leni1 (因为我们想尽量向右取),kx>0 的部分向右平移 leni ,然后最后全局加上 gi 就行了。

我们其实可以用 Splay 维护这个凸壳,每次只需要支持区间加一次函数,全局平移即可。

但显然可以更方便地解决,由于最后我们只需要求 k=0 时候的函数值,我们利用对顶堆维护 k<0,k>0 的位置,每次讨论一下插入的绝对值函数的 0 点位置即可。

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

总结

对于加绝对值函数,并且取 min,maxdp 都可以考虑是否存在凸壳,然后通过 线段树/ 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 个人是不友好的。每次你可以问 xy 是不是一个诚实的人。如果 x 是诚实的,那么一定会回答真正的答案。否则,他会随便告诉你一个结果。(交互库有一定策略地回答。)

现在告诉你 a,b ,你需要确定是否一定能问出来。如果问不出来输出 Impossible 。如果能问出来,需要在 2n 步内问出来。

题解

首先我们考虑什么时候是 Impossible ,显然当 ba 的时候,b 可以很好的隐藏在 a 中。因为问任意一个人,b 都可以根据 a 的决策,来颠倒黑白。只有当 a 超过 n 的一半的时候,我们问任意一个人都可以根据 Y, N 中较多的那项确定类别。

接下来,我们不难想到一个乱搞。就是一开始随机问几个人,然后问所有人他的类别,就可以确定类别了。如果是老实人,然后就可以一遍问它就能得到所有人的类别了。我们打乱一下询问顺序,那么这样期望下是 3n 的。

我们其实可以继续优化一下乱搞,加上如下几个优化:

  • 如果问出当前人的类别,之前回答类别不同的人,肯定不是老实人,之后我们全都跳过不问即可。
  • 如果我们当前问的 Y, N 其中较多的那个个数,大于剩下没有确定的不友好的人数,就可以确定这个人的类别了。
  • 如果当前只剩下友好/不友好,我们就可以直接不问,然后确定即可。

期望应该是 1.5n 的?然后全都加上。。就可以过啦qwq(我也是交了十几发才艹过的。。)


显然有确定性做法,我们需要基于这样一个结论,如果 xy 是不友好的,那么 x,y 肯定不可能同时是诚实的,如果我们忽略他们,剩下的老实人个数仍然大于一半。

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

然后最后剩下了 a0,,ak1 其中每个 ai 都说 ai+1 是诚实的,那么显然 ak1 一定是诚实的。为什么呢?因为其中一定有个人是老实人,那么在它后面的所有人一定都是老实人,那么最后一个人必是老实人。

然后我们就可以在稳定的 2n 次里问出所有人的类别啦。(好妙啊~)

代码

放个瞎JB乱搞

ARC 072

F - Dam

题意

有一个容量为 L 的水库,每天晚上可以放任意体积的水。每天早上会有一定温度和体积的水流入水库,且要保证流入水之后水的总体积不能超过 L 。令体积分别为 V1,V2 ,温度分别为 t1,t2 的水混合后的温度为 V1t1+V2t2V1+V2 。初始水库为空。现给出 n 天流入水的体积和温度,分别最大化每一天中午水库满容量时的水温。

1n5×105

题解

一道很有意思的题~

我们可以发现两个性质:

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

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

至于为什么是对的呢?你可以考虑把每个水看做一个向量,我们相当于看向量和的斜率,我们其实就是需要贪心地维护一个下凸壳,本质上是一样的。

代码

Submission #5206287

ARC 073

E - Ball Coloring

题意

n 个盒子,每个盒子里面有两个球,分别写了一个数字 xi,yi 。现在需要把每个盒子其中的一个球染成红色,另外一个染成蓝色。

Rmax 为红球数字最大值,其他的同理,求 (RmaxRmin)(BmaxBmin) 的最小值。

n2×105

题解

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

令全局 xi,yi 最大值为 Amax ,最小值为 Amin 。显然 Rmax,Bmax 其中一个需要取到 Amaxmin 同理。

我们考虑分两种情况讨论。

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

    不妨令 Rmax=Amax,Bmin=Amin ,那么我们显然需要最小化 Bmax ,最大化 Rmin

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

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

    不妨令 Rmax=Amax,Rmin=Amin ,那么我们就需要最小化 BmaxBmin

    我们考虑从小到大枚举 Bmin ,然后动态维护 Bmax 的最小值。

    如果当且 Bmin=Amin ,我们显然 Bmax 取到所有的 min{xi,yi} 的最大值是最优的。

    然后我们每次把 Bmin 变大,也就是翻转 Bmin 的颜色,随便维护一下最值即可。

O(nlogn)

代码

Submission #5212035

F - Many Moves

题意

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

1n,q2×105

题解

唯一一道自己做出来的 F TAT 虽然很水。

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

但显然每次我们有个盒子的位置一定在 xp1 ,我们只需要记 fi 为其中一个盒子在 xp1 ,另外一个在 i 的最少距离和。

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

  • ixp1 ,显然不动 i ,动 xp1 即可,所以有 fi=fi+|xpxp1|
  • 对于 i=xp1 我们考虑枚举上次的另外一个位置 j ,那么有 fxp1=minj{fj+|xpj|}

直接实现是 O(n2) 的,对于第一个转移就是全局加,对于第二个转移拆绝对值,然后维护 fi±i 的最小值即可。

都可以用线段树实现 O(nlogn)

代码

Submission 5230528

ARC 075

F - Mirrored

题意

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

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

1D<109

题解

考虑固定长度为 L+1 ,假设从低到高每一位分别是 bi ,那么其实就是

rev(N)N=i=0L(10Li10i)bi=i=0L2(10Li10i)(bibLi)

我们等价求把 0L2 的每个 vi=10Li10i 乘上 99 和为 D 的方案数。(对于每个 99 对应了两个数的一种组合)。

直接 dfs 可能会 TLE ,考虑利用性质优化,我们观察到:

vi>j=i+1L29vj

那么意味着如果我们确定前 i 位,组合出来的数与 D 相差 vi 时,显然是以后无论如何也无法恢复的。

那么每一步其实我们的填法从 18 降低到只有 2 种了。

我们需要枚举的长度应该是 LD2LDLDD 十进制的长度),因为我们加减的范围应该是刚好 LD 的,超过 2LD 加减最小数已经超过了 D 显然无法得到。

有科学的复杂度为

O(i=LD2LD2i2)=O(2LD)=O(2log10D)=O(2log2Dlog210)=O(Dlog102)O(D0.3010)

跑的挺快的。

代码

#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 个点的有向环套树,需要对于每个点定取值 ai0 ,满足。

  • 对于所有边 (i,j)aiaj
  • 对于 0x<ai 都存在至少一条边 (i,j) 使得 aj=x

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

2n2×105

题解

其实操作本质上其实就是个 mex ,对于树上的 mex ,每个节点的值应该是确定的。

需要考虑环,处理完所有环上节点的子树,就得出了每个环上节点的取值下界 bi

  1. 所有 bi 相同且环长度为偶数:我们隔一个数就把当前数加 1 即可。

  2. 所有 bi 相同且环长度为奇数:那么隔一加一的就不行了,其实不难发现这样无法构造出合法方案。

  3. 存在有 bi 不同:找到 bi 最小的点 v 把它 +1 ,然后依次考虑 uv 然后如果 bu=bv+1 那么我们继续操作即可,不难发现这样操作一定会在某点停止不会绕圈。

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

代码

Submissions 5475398


__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/10730876.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(640)  评论(2编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
历史上的今天:
2018-04-18 BZOJ5286: [Hnoi2018]转盘 (线段树)
点击右上角即可分享
微信分享提示