日记

日记

周二 3月 25 2025
983 字 · 6 分钟

昨天祝老问我 dp 要不要加难度,我说加。

Modular Sequence

我们看到 aia_i 可以是 ai1modya_{i - 1} \bmod y 也可以是 ai1+ya_{i - 1} + y。那么肯定有 a1a2a3(mody)a_1 \equiv a_2 \equiv a_3 \dots \pmod y,于是我们判断无解首先就判断 s(xmody)×n(mody)s \equiv (x \bmod y) \times n \pmod y。然后我们设 ansi=ai×y+(xmody)ans_i = a_i \times y + (x \bmod y)。那么 aa 数组肯定形如 {st,st+1,st+2,,st+k0,0,1,2,3,,k1,0,1,2,3,,k2,,0,1,2,3,,k?}\{st,st + 1,st + 2,\dots,st + k_0,0,1,2,3,\dots,k_1,0,1,2,3,\dots,k_2,\dots,0,1,2,3,\dots,k_?\}。且 i=1nai=s(xmody)×ny\sum\limits_{i = 1}^{n}a_i = \frac{s - (x \bmod y) \times n}{y}

然后我尝试贪心构造 aa,成功的 wrong answer Solution exists, but participant said No (test case 3970)。这个 3970 就很绝望。

正解是 dp,设 dpsdp_s 表示使得 i=1nai=s\sum\limits_{i = 1}^{n}a_i = s 的最小 nn。我们可以通过枚举最后一段的长度来转移。枚举状态 O(n)O(n),转移 O(n)O(\sqrt{n})。合起来 O(nn)O(n\sqrt{n})。然后根据它来判断是否可行。构造合法序列就看它从哪里转移的就行了。

Distance to Different

诶您猜怎么着?赛时我都想到 80%80\% 了,然后放弃去看上面的那道了。

我们发现 bb 数组一定可以分成几段,最左边的一段单调递减,最右边的一段单调递增。中间的先递增再递减。于是我们设 dpi,jdp_{i,j} 表示分 jj 段,最后一段结尾为 ii 的方案数。我们发现,如果一段长度为 22 的区间其实和两段长度为 11 的区间形态一样,于是我们规定中间的段长度不能为 22。于是我们有转移:dpi,j=k=0i1dpk,j1dpi2,j1dp_{i,j} = \sum\limits_{k = 0}^{i - 1}dp_{k,j - 1} - dp_{i - 2,j - 1},注意特判第一段和最后一段不需要减 dpi2,j1dp_{i - 2,j - 1}

GCD on a grid

hrl:祝老特有的不识数,1600 ~ 2000 的题里面放 2100,2100 ~ 2400 的题里面放 1900。

我:你就偷着乐吧!

我们发现路径一定经过 a1,1a_{1,1}。于是我们考虑循环 a1,1a_{1,1} 的每一个因数 ii。看看是否有一条路径只经过模 ii00 的数。时间复杂度 O(n2)O(n^2)。带一个 240240 左右的常数。因为 1×1061 \times 10^6 的数量级因数最多就 240240 多个。

Learning to Paint

其实老早以前就看过这道题。但是没做。

我们发现,一个位置 jj 如果要转移 ii,那么贡献一定是 aj+2,ia_{j + 2,i},这是不变的。我们就可以设 dpidp_i 为将前 ii 个数分段的最大的 kk 个数。转移就是这样:先从 j[1,i),dpj\forall j \in [-1,i),dp_{j} 里取出最大的放进备选里,然后执行 kk 次,每次从备选里去最大的一个,并把它接着的下一个放进备选。

还是放一下代码:

#include<bits/extc++.h>
#define int long long
using namespace std;
typedef pair<int,int> pii;
int n,m;
int a[1005][1005];
queue<int> tmp[1005];
priority_queue<pii> pend;
priority_queue<int> dp[1005];
template<typename typ>
void clear(priority_queue<typ> &q){while (!q.empty())q.pop();}
int get(int x){int res = dp[x].top();dp[x].pop();tmp[x].push(res);return res;}
void solve()
{
scanf("%lld%lld",&n,&m);
for (int i = 0; i <= n; i++)
clear(dp[i]);
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j++)
scanf("%lld",a[i] + j);
dp[0].push(0);
for (int i = 1; i <= n; i++)
{
clear(pend);
pend.push({a[1][i],-1});// 放进备选
for (int j = 0; j < i; j++)
pend.push({get(j) + a[j + 2][i],j});// 放进备选
for (int j = 1; j <= m && !pend.empty(); j++)
{
auto [val,id] = pend.top();// 取出最大值
pend.pop();
dp[i].push(val);// 转移
if (~id && !dp[id].empty())
pend.push({get(id) + a[id + 2][i],id});// 将它的下一个放进备选
}
for (int j = 0; j < i; j++)
{
while (!tmp[j].empty())
{
dp[j].push(tmp[j].front());
tmp[j].pop();
// 别忘了还原
}
}
}
while (m--)
{
printf("%lld%c",dp[n].top(),m ? ' ' : '\n');
dp[n].pop();
}
}
signed main()
{
int t;
scanf("%lld",&t);
while (t--)
solve();
return 0;
}

这玩意的 clear 调了我 3030 min。究其原因是没传引用。

这个喷不了这个是纯糖。

后日谈

yzp 在做图论简单题。然而这些题我老早就做过了。

但是黄色的题啊,我一眼就看出来了,而且 O(n2)O(n^2) 都能过的题,他看不出来???

然后尝试理解线性基,只能感叹数学的神奇。


Thanks for reading!

日记

周二 3月 25 2025
983 字 · 6 分钟