【luogu P6891】ビルの飾り付け 4(DP)(结论)

ビルの飾り付け 4

题目链接:luogu P6891

题目大意

给你两个长度为 2n 的序列,然后你要凑一个 2n 的序列每个位置可以从两个序列中对于的位置选一个。
然后要你保证得到的序列单调不降且两个序列各用了 n 个。
要你判断无解或输出其中一个合法方案。

思路

正题

首先一个显然的 DP 设 fi,j,0/1 为前 i 个其中第一个序列用了 j 个最后一个第一个 / 第二个序列是否有答案。
然后这是 n2 的,考虑搞点性质。

观察连边的样子,会发现会存在这么一些限制。
有一些地方固定只能选一个,有一些地方选了某个后面跟着不能选某个。
(就小的可以连两个,大的只能连一个,我们从不能连的角度来看就是这样)

前面的很好搞,考虑后面的限制,那整个图肯定可以分成若干个互补联系的限制,然后联系的限制会形成一条链,具体一下就是链上的相邻两个不能同时选。
那对于每个链选的量就是一个区间,那若干个区间加起来也是一个区间,所以我们可以得到这么一个结论:
两个序列可以选的数量都是在一个区间之中的。

那由于互补,我们可以分别 DP 出 fi,0/1,0/1 为前 i 个最后选的是第一个序列或第二个,第一个序列或第二个序列最多能选多少个。
所以只要两个序列的最大值都大于要求,也就落在了区间中。

那我们要求方案,我们就倒推,如果选了还能满足条件就选,然后如果选哪个都不满足就是无解。

ex口胡

牛客有道题是这个的是这个的升级版,就是要求合法选择方案,这里口胡一下懒得写了。

那我们继续从证明的地方来看,我们对于每段链来考虑,假设长度为 x,我们要在其中选 y 个,那插板法一下就是:
(xy+1y)(减掉的放在你选的后面强制不选)

然后你把这些弄成多项式,然后不难看出就是几个多项式乘起来取某一项,直接分治 + NTT 即可。

代码

#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int N = 1e6 + 100; int n, a[N][2], f[N][2][2]; char ans[N]; int main() { scanf("%d", &n); for (int i = 1; i <= n << 1; i++) scanf("%d", &a[i][0]); for (int i = 1; i <= n << 1; i++) scanf("%d", &a[i][1]); memset(f, -0x7f, sizeof(f)); f[1][0][0] = 1; f[1][0][1] = 0; f[1][1][0] = 0; f[1][1][1] = 1; for (int i = 2; i <= n << 1; i++) { if (a[i - 1][0] <= a[i][0]) { f[i][0][0] = max(f[i][0][0], f[i - 1][0][0] + 1); f[i][0][1] = max(f[i][0][1], f[i - 1][0][1]); } if (a[i - 1][1] <= a[i][0]) { f[i][0][0] = max(f[i][0][0], f[i - 1][1][0] + 1); f[i][0][1] = max(f[i][0][1], f[i - 1][1][1]); } if (a[i - 1][0] <= a[i][1]) { f[i][1][0] = max(f[i][1][0], f[i - 1][0][0]); f[i][1][1] = max(f[i][1][1], f[i - 1][0][1] + 1); } if (a[i - 1][1] <= a[i][1]) { f[i][1][0] = max(f[i][1][0], f[i - 1][1][0]); f[i][1][1] = max(f[i][1][1], f[i - 1][1][1] + 1); } } int numa = 0, numb = 0, lst = 2e9; for (int i = n << 1; i >= 1; i--) { if (numa + f[i][0][0] >= n && numb + f[i][0][1] >= n && a[i][0] <= lst) { numa++; ans[i] = 'A'; lst = a[i][0]; } else if (numa + f[i][1][0] >= n && numb + f[i][1][1] >= n && a[i][1] <= lst) { numb++; ans[i] = 'B'; lst = a[i][1]; } else { printf("-1"); return 0; } } for (int i = 1; i <= n << 1; i++) putchar(ans[i]); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P6891.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示