P6891 [JOISC2020] ビルの飾り付け 4

P6891 [JOISC2020] ビルの飾り付け 4

题目传送门

Problem

给定长度为 2n1n5×105)的序列 AB。要求构造一个单调不降的序列 C。每个 CiAiBi 中选取,且 AB 中都恰好选取 n 个。

Solution

最直接的想法显然是 dp ,设 fi,j,0/1 表示考虑了 C 的前 i 位,选了 jA 中的,第 i 位选的是 Ai/Bi 是否可行。

但这样设计状态很明显时空复杂度都是会爆的。

这里用一个很经典的技巧——交换 dp 数组的定义域和值域,也就是交换下标和值。

考虑 dpi,0/1 表示考虑了 C 的前 i 位,第 i 位选 Ai/Bi 时选 A 中元素个数的可行集合,也就是使 fi,j,0/1 成立的 j 的集合。

普通的集合是很难转移的,但如果这恰好是区间的话那就可以用左右端点来表示和转移。所以我们尝试能不能证明 dp 数组表示的是区间。

先想想怎么转移,dpi,0/1 显然由 dpi1,0dpi1,1 转移过来,要么是他们的并集,要么是其中一个,可能再整体加一。

再来看 dp 数组的边界,显然 dp1,0={1}dp1,1={0},都是区间。而 A1Bi 中大的那个的可行集合一定包含小的那个的可行集合。所以 {x|x1dpi,0}dpi,1 一定是包含关系,故若两者都是区间,则一定相邻或相交,两者取并也是区间。

这样我们就可以用左右端点来表示 dp 数组的值。

AC Code

#include <bits/stdc++.h>
namespace _default{
    using namespace std;
    #define lovely return
    #define _lzy_ 0
    #define LL long long
    #define int long long
    #define DB double
    #define PII std::pair<int, int>
    #define X first
    #define Y second
    #define uF(i, x, y) for(int i = (x); i <= (y); ++ i)
    #define uf(i, x, y) for(int i = (x); i < (y); ++ i)
    #define dF(i, x, y) for(int i = (x); i >= (y); -- i)
    #define df(i, x, y) for(int i = (x); i > (y); -- i)
    #define ef(i, u) for(int i = head[(u)]; i; i = ne[i])
    #define sett(x, y) memset(x, y, sizeof x)
    #define copy(x, y) memcpy(x, y, sizeof x)
    const int MOD = 1e9 + 7;
    const DB EPS = 1e-8, INF = 0x3f3f3f3f;
    template<typename T> inline T read(){
        T s = 0; int f = 0; char ch = getchar();
        while(!isdigit(ch)){if(ch == '-') f = 1; ch = getchar();}
        while(isdigit(ch)) s = s * 10 + ch - 48, ch = getchar();
        return f ? ~s + 1 : s;
    }
    template<typename T> inline void write(T x, const char *s = ""){
        static int st[40]; int top = 0;
        if(x < 0){putchar('-'); x = -x;}
        if(!x) putchar('0');
        while(x) st[++ top] = x % 10, x /= 10;
        while(top) putchar(st[top --] + 48);
        printf("%s", s);
    }
    template<typename T> inline void updmin(T &x, T y){if(x > y) x = y;}
    template<typename T> inline void updmax(T &x, T y){if(x < y) x = y;}
    template<typename T> inline void updadd(T &x, T y){(x += y % MOD) %= MOD;}
    template<typename T> inline void updmul(T &x, T y){(x *= y % MOD) %= MOD;}
    int cmp(DB a, DB b){if(fabs(a - b) < EPS) return 0; return a - b > EPS ? 1 : -1;}
}
using namespace _default;
const int N = 1e6 + 5;
int n, m, a[N], b[N], ans[N];
PII f[N][2];
signed main(){
    n = read<int>(), m = n << 1;
    uF(i, 1, m) a[i] = read<int>();
    uF(i, 1, m) b[i] = read<int>();
    f[1][0] = {1, 1}, f[1][1] = {0, 0};
    uF(i, 2, m){
    	f[i][0] = f[i][1] = {INF, -INF};
    	if(a[i] >= a[i - 1]) updmin(f[i][0].X, f[i - 1][0].X), updmax(f[i][0].Y, f[i - 1][0].Y);
    	if(a[i] >= b[i - 1]) updmin(f[i][0].X, f[i - 1][1].X), updmax(f[i][0].Y, f[i - 1][1].Y);
    	if(b[i] >= a[i - 1]) updmin(f[i][1].X, f[i - 1][0].X), updmax(f[i][1].Y, f[i - 1][0].Y);
    	if(b[i] >= b[i - 1]) updmin(f[i][1].X, f[i - 1][1].X), updmax(f[i][1].Y, f[i - 1][1].Y);
    	++ f[i][0].X, ++ f[i][0].Y;
	}
	int cur = n, cs = INF;
	dF(i, m, 1){
		if(f[i][0].X <= cur && f[i][0].Y >= cur && a[i] <= cs) -- cur, cs = a[i];
		else if(f[i][1].X <= cur && f[i][1].Y >= cur && b[i] <= cs) ans[i] = 1, cs = b[i];
		else{
			puts("-1");
			return 0;
		}
	}
	uF(i, 1, m) putchar(ans[i] ? 'B' : 'A');
	puts("");
    lovely _lzy_;
}

posted @   牛肉爱吃dks  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示