首页 > 其他分享 >Gym 104855E Perfect Permutation

Gym 104855E Perfect Permutation

时间:2024-02-14 11:11:48浏览次数:20  
标签:Perfect std int max Gym i64 maxn 104855E 序列

考虑最后对于每个 \(i\) 是选 \(a_i, b_i, c_i\) 之中哪一个的序列。
通过观察能发现序列去掉 \(b\) 后满足开头为 \(c\) 末尾为 \(a\) 这个序列就是合法的,同时整个序列都为 \(b\) 也是合法的。

首先如果是个合法序列,对于去掉 \(b\) 后的开头,其余不是 \(b\) 的下标肯定比其大,所以只能是 \(c\),末尾只能是 \(a\) 同理。
然后对于去掉 \(b\) 后满足开头为 \(c\) 末尾为 \(a\) 的序列,考虑证明一定可以构造出对应的 \(p\)。
\(b\) 显然是简单的不考虑,只考虑 \(a, c\)。
把 \(a, c\) 的序列拆为多个满足前面都是 \(c\) 后面都是 \(a\) 的段,一个段一个段来处理。
首先令 \(p_i = i\),接下来对于这个段内的下标循环右移 \(1\) 位,能发现变成了第一个为 \(c\) 其余的为 \(a\),继续能发现循环右移了 \(x\) 位开头就有 \(x\) 个 \(c\)。
那只需根据开头的 \(c\) 的数量循环右移对应位数即可。

对于答案,就可以考虑 \(\text{DP}\)。
令 \(f_{i, 0 / 1}\) 为前 \(i\) 位是否出现了开头为 \(c\) 的最优值,\(g_{i, 0 / 1}\) 为考虑第 \(i\) 位及之后是否出现了末尾为 \(a\) 的最优值。
那么最后答案的选取就分为全选 \(b\) 或者前面为 \(c\) 后面为 \(a\) 拼起来,即 \(\max\{\sum\limits_{i = 1}^n b_i, \max\limits_{i = 1}^{n - 1} f_{i, 1} + g_{i + 1, 1}\}\)。
然后按照上面提到的方式构造即可。

时间复杂度 \(O(n)\)。

#include<bits/stdc++.h>
using i64 = long long;
const i64 inf = 0x3f3f3f3f3f3f3f3f;
const int maxn = 2e5 + 10;
int a[maxn], b[maxn], c[maxn];
i64 f[maxn][2]; int lf[maxn];
i64 g[maxn][2]; int lg[maxn];
int p[maxn], op[maxn];
inline void solve() {
    int n; scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
    for (int i = 1; i <= n; i++) scanf("%d", &c[i]);
    for (int i = 0; i <= n + 1; i++) for (int j : {0, 1}) f[i][j] = g[i][j] = -inf;
    f[0][0] = g[n + 1][0] = 0;
    for (int i = 1; i <= n; i++) {
        f[i][0] = f[i - 1][0] + b[i], f[i][1] = f[i - 1][0] + c[i], lf[i] = 0;
        i64 v = f[i - 1][1] + std::max({a[i], b[i], c[i]});
        if (v > f[i][1]) f[i][1] = v, lf[i] = 1;
    }
    for (int i = n; i; i--) {
        g[i][0] = g[i + 1][0] + b[i], g[i][1] = g[i + 1][0] + a[i], lg[i] = 0;
        i64 v = g[i + 1][1] + std::max({a[i], b[i], c[i]});
        if (v > g[i][1]) g[i][1] = v, lg[i] = 1;
    }
    i64 ans = 0; int w = 0;
    for (int i = 1; i <= n; i++) ans += b[i];
    for (int i = 1; i < n; i++) {
        i64 v = f[i][1] + g[i + 1][1];
        if (v > ans) ans = v, w = i;
    }
    for (int i = 1; i <= n; i++) p[i] = i;
    if (w) {
        for (int i = w, j = 1; i; i--) {
            if (! j) {op[i] = 0; continue;}
            if (! lf[i]) {j = 0, op[i] = 1; continue;}
            op[i] = a[i] >= std::max(b[i], c[i]) ? 2 : (b[i] >= std::max(a[i], c[i]) ? 0 : 1);
        }
        for (int i = w + 1, j = 1; i <= n; i++) {
            if (! j) {op[i] = 0; continue;}
            if (! lg[i]) {j = 0, op[i] = 2; continue;}
            op[i] = a[i] >= std::max(b[i], c[i]) ? 2 : (b[i] >= std::max(a[i], c[i]) ? 0 : 1);
        }
        for (int l = 1; l <= n; l++) {
            if (! op[l]) continue;
            std::vector<int> W;
            bool fl = 1; int cnt = 0;
            for (int r = l; r <= n && (fl || op[r] != 1); r++) {
                if (! op[r]) continue;
                W.push_back(r), op[r] == 1 ? (cnt++) : (fl = 0); 
            }
            for (int i = 0, j = W.size() - cnt; i < int(W.size()); i++, (++j) %= W.size()) p[W[i]] = W[j];
            for (int i : W) op[i] = 0;
        }
    }
    for (int i = 1; i <= n; i++) printf("%d ", p[i]); printf("\n");
}
int main() {
    int T; scanf("%d", &T);
    while (T--) solve();
    return 0;
}

标签:Perfect,std,int,max,Gym,i64,maxn,104855E,序列
From: https://www.cnblogs.com/rizynvu/p/18015083

相关文章

  • CF1338C Perfect Triples 题解
    解题思路没什么好说的,就是打表找规律……表在这里不难发现,三元组中第一个数的最后两位按照\(00\to01\to10\to11\)的顺序变化,其他位也一样,同样,第二个数和第三个数中每两位分别按照\(00\to10\to11\to01\)和\(00\to11\to01\to10\)的顺序变化,且与第一个数对应变化......
  • CF1603E A Perfect Problem
    一个完美的序列满足任何非空子序列的最大值与最小值乘积大于等于其总和,求长度为\(n\),值域为\([1,n+1]\)的完美序列个数,对质数\(M\)取模。\(n\le200\)给这个序列排序后,注意到如果所有前缀合法,那么任何子序列都合法。一个观察是,如果第一个数太小,那么总是无解。设第一个数......
  • Gymnasium 环境搭建
    【默认在链接公网环境】!!!!一、     Conda虚拟环境搭建【安装则忽略】1.1检查本地适配python版本>python-V1.2根据版本下载并安装aconda【这里默认使用window平台】:1.3测试conda安装并创建虚拟环境:我这里使用版本为3.8的虚拟环境语法:condacreate-nenv_namepyt......
  • UVA1109/Gym101175I Mummy Madness
    题意简述你初始在\((0,0)\),每个时刻你能向八连通格子移动或不移动。有\(n\)个怪物,怪物坐标已知,每个时刻怪物也能向八连通格子移动或不移动,而且会选择最终与你欧几里得距离最短的一种方案。求你在什么时刻会被怪物抓住(你和怪物在同一格子内),或报告无解。\(n\le10^5,|x_i|,|y......
  • Gym104095L 送外卖
    https://codeforces.com/gym/104095/attachments/download/18184/statements.pdf首先这个\(n\le14\)的数据范围可以直接考虑状压了。设\(f_{i,S,time}\)为当前骑手在\(i\)号城市,已经把外卖送给了状态为\(S\)的城市,此时的时间为\(times\)所能获得的最大收益。当\(time......
  • Gym104270E Kawa Exam
    题意简述有\(n\)道题,每道题有\(10^5\)个选项,其中选项\(a_i\)是正确的。再给定\(m\)条限制\(u_i,v_i\),表示题目\(u_i,v_i\)必须要选择相同的选项。对于\(m\)条限制,求出若去掉这条限制,最多能回答多少问题。多组数据。\(n,m,a_i\le10^5,\sumn,\summ\le10^6\)。......
  • GYM102596L Yosupo's Algorithm【分治,支配对】
    给定平面上\(2n\)个点,每个点有坐标\((x_i,y_i)\),权值\(w_i\)及颜色\(c_i\)。所有点满足:若\(c_i=0\),则\(x_i<0\);若\(c_i=1\),则\(x_i>0\)。\(q\)次查询,每次给定\(L_i,R_i\),你需要选择两个点\(i,j\)满足如下条件:\(c_i=0,c_j=1\)。\(x_i<L,x_j>R\)或\(x_......
  • 【题解】gym103743 (2022 JSCPC)
    A.PENTAKILL!考虑直接模拟,规则就是一个人将其他人全部都击杀,并且中间没有重复击杀。code:#include<bits/stdc++.h>usingnamespacestd;map<string,vector<string>>st;intn;stringa,b;intmain(){cin>>n;for(inti=1;i<=n;++i){ci......
  • gym103415A Math Ball
    套路生成函数。写出答案的式子,设\(f_i(x)=\sumj^{c_i}x^j\),不难得到答案为:\[[x^W]{1\over1-x}\prod_{i=1}^nf_i(x)\]考虑求\(f_i(x)\)。看到指数上有\(c_i\),想到用斯特林数展开:\[f_i(x)=\sum_{j=0}^{\infty}x^j\sum_{k=0}^{c_i}{c_i\bracek}\binom{j}{k}k!\]\[=\s......
  • 题解 Gym 102341B【Bulbasaur】/ SS231107C【爬梯高手】
    题解SS231107C【爬梯高手】撞原了,好耶!Gym102341B顺便把我的变异加强版爆标了!!!problem有一个\(n*m\)个点的有向分层图,共有\(n\)层,每层\(m\)个点,每条边一定是从第\(i\)层连向第\(i+1\)层。定义\(f(i,j)\)表示选择若干条路径,每条路径从第\(i\)层出发,在第\(j\)......