题目大意

给出一个长度为\(n\)的排列\(P\)与一个正整数\(k\).

你需要进行如下操作任意次, 使得排列\(P\)的字典序尽量小.

对于两个满足\(|i-j|>=k\) 且\(|P_i-P_j| = 1\) 的下标\(i\)与\(j\),交换\(P_i\) 与\(P_j\).

解题思路

若构造\(Q_{p_i}=i\), 即\(Q_i\)表示\(i\)在\(P\)序列中的位置, 则容易发现, 当\(Q\)的字典序最小的时候, \(P\)的字典序就达到了最小.

于是可以把原问题转换成求最小的\(Q\)的字典序. 根据题目中的条件, 稍加思考把原来的交换操作定义到序列\(Q\)上: 对于两个满足\(|i-j|=1\)且\(|Q_i-Q_j|>=k\)的下标\(i\)与\(j\), 交换\(Q_i\)与\(Q_j\).

那么我们考虑, 对于怎样的\(i\)和\(j\), 它们经过任意次操作之后的字典序的相对位置都不变.

对于每一个\(L\), 它最右能够交换到的位置就是最大的\(R\)满足对于\(\forall j \in [i,R]\)都有\(|Q_L-Q_j|>=k\). 于是\(R+1\),也就是最小的\(R'\)满足\(|Q_L-Q_{R'}|<k\),它们\((L\text{与}R')\)的相对顺序是不会变的. 显然这个东西可以通过线段树做到\(O(n\log{n})\).

于是对于每一个\(L\), 找到对应的\(R'\), 连一条边\((R',L)\), 表示\(R'\)一定在\(L\)之后. 对这个图拓扑排序, 就得到了字典序相对顺序的倒序. 倒着做一遍, 在过程中使用大根堆维护即可就出最小的字典序. 最后还原即可.

时间复杂度\(O(n\log{n})\).

后记

有一说一, 自己对这种题还是缺乏基本的经验. 对于拓扑排序确定大小顺序的题也缺乏一定的转化能力和理解能力. 这种题之后遇到了如果没做出来也要整理.

#include <queue>
#include <cstdio>
#include <cstring>
#define N 500010
#define INF 0x3f3f3f3f
#define init(a, b) memset(a, b, sizeof(a))
#define fo(i, a, b) for(int i = (a); i <= (b); ++i)
#define fd(i, a, b) for(int i = (a); i >= (b); --i)
using namespace std;
inline int read()
{
int x = 0; char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return x;
}
inline int min(int a, int b){return a < b ? a : b;}
int n, k, p[N], q[N]; priority_queue<int> h; int in[N], last[N], pre[N << 1], to[N << 1];
inline void add(int u, int v){static int tot = 0; ++tot, ++in[v], to[tot] = v, pre[tot] = last[u], last[u] = tot;} namespace SGT
{
#define ls t << 1
#define rs ls | 1
#define mid ((l + r) >> 1)
int tr[N << 4];
inline void pushup(int t){tr[t] = min(tr[ls], tr[rs]);}
void build(int t, int l, int r)
{
if(l == r) return (void)(tr[t] = INF, 0);
build(ls, l, mid); build(rs, mid + 1, r);
pushup(t);
}
void update(int t, int l, int r, int w, int v)
{
if(l == r) return (void)(tr[t] = v, 0);
w <= mid ? update(ls, l, mid, w, v) : update(rs, mid + 1, r, w, v);
pushup(t);
}
inline void update(int w, int v){update(1, 1, n, w, v);}
int query(int t, int l, int r, int fl, int fr)
{
if(fl <= l && r <= fr) return tr[t];
int ret = INF;
fl <= mid && (ret = min(ret, query(ls, l, mid, fl, fr)));
fr > mid && (ret = min(ret, query(rs, mid + 1, r, fl, fr)));
return ret;
}
inline int query(int fl, int fr){return query(1, 1, n, fl, fr);}
#undef ls
#undef rs
#undef mid
}
int main()
{
freopen("permutation.in", "r", stdin);
freopen("permutation.out", "w", stdout);
scanf("%d %d", &n, &k);
fo(i, 1, n) scanf("%d", &p[i]), q[p[i]] = i;
SGT::build(1, 1, n);
fd(i, n, 1)
{
int pos = SGT::query(q[i], min(q[i] + k - 1, n));
pos < INF && (add(q[pos], q[i]), 0);
pos = SGT::query(max(1, q[i] - k + 1), q[i]);
pos < INF && (add(q[pos], q[i]), 0);
SGT::update(q[i], i);
}
fo(i, 1, n)
if(!in[i])
h.push(i);
fd(w, n, 1)
{
int u = h.top(); h.pop(); p[w] = u;
for(int i = last[u]; i; i = pre[i])
if(!(--in[to[i]])) h.push(to[i]);
}
fo(i, 1, n) q[p[i]] = i;
fo(i, 1, n) printf("%d\n", q[i]);
return 0;
}

JZOJ5405 & AtCoder Grand Contest 001 F. Permutation的更多相关文章

  1. Atcoder Grand Contest 001 F - Wide Swap(拓扑排序)

    Atcoder 题面传送门 & 洛谷题面传送门 咦?鸽子 tzc 来补题解了?奇迹奇迹( 首先考虑什么样的排列可以得到.我们考虑 \(p\) 的逆排列 \(q\),那么每次操作的过程从逆排列的 ...

  2. Atcoder Grand Contest 030 F - Permutation and Minimum(DP)

    洛谷题面传送门 & Atcoder 题面传送门 12 天以前做的题了,到现在才补/yun 做了一晚上+一早上终于 AC 了,写篇题解纪念一下 首先考虑如果全是 \(-1\)​ 怎么处理.由于我 ...

  3. AtCoder Grand Contest 001 C Shorten Diameter 树的直径知识

    链接:http://agc001.contest.atcoder.jp/tasks/agc001_c 题解(官方): We use the following well-known fact abou ...

  4. AtCoder Grand Contest 002 F:Leftmost Ball

    题目传送门:https://agc002.contest.atcoder.jp/tasks/agc002_f 题目翻译 你有\(n*k\)个球,这些球一共有\(n\)种颜色,每种颜色有\(k\)个,然 ...

  5. AtCoder Grand Contest 001 D - Arrays and Palindrome

    题目传送门:https://agc001.contest.atcoder.jp/tasks/agc001_d 题目大意: 现要求你构造两个序列\(a,b\),满足: \(a\)序列中数字总和为\(N\ ...

  6. AtCoder Grand Contest 017 F - Zigzag

    题目传送门:https://agc017.contest.atcoder.jp/tasks/agc017_f 题目大意: 找出\(m\)个长度为\(n\)的二进制数,定义两个二进制数的大小关系如下:若 ...

  7. AtCoder Grand Contest 003 F - Fraction of Fractal

    题目传送门:https://agc003.contest.atcoder.jp/tasks/agc003_f 题目大意: 给定一个\(H×W\)的黑白网格,保证黑格四连通且至少有一个黑格 定义分形如下 ...

  8. AtCoder Grand Contest 011 F - Train Service Planning

    题目传送门:https://agc011.contest.atcoder.jp/tasks/agc011_f 题目大意: 现有一条铁路,铁路分为\(1\sim n\)个区间和\(0\sim n\)个站 ...

  9. AtCoder Grand Contest 010 F - Tree Game

    题目传送门:https://agc010.contest.atcoder.jp/tasks/agc010_f 题目大意: 给定一棵树,每个节点上有\(a_i\)个石子,某个节点上有一个棋子,两人轮流操 ...

随机推荐

  1. Mysql不锁表备份之Xtrabackup的备份与恢复

    一.Xtrabackup介绍 MySQL冷备.热备.mysqldump都无法实现对数据库进行增量备份.如果数据量较大我们每天进行完整备份不仅耗时且影响性能.而Percona-Xtrabackup就是为 ...

  2. 【Linux】【Services】【SaaS】Docker+kubernetes(11. 构建复杂的高可用网络)

    1. 简介 flannel在实战阶段貌似不能胜任在灾难恢复时候异地的网络,打算用openvswith试试

  3. Boss直聘App上“天使投资、VC、PE” 与“A轮、B轮、C轮融资”的关系

    我们经常看到朋友圈里某某公司获得了某轮融资,所谓的A轮B轮究竟是个什么概念呢?今天就跟小伙伴们分享一下A.B.C.D轮融资与天使投资.VC.PE的关系. 天使投资(AI):天使投资所投的是一些非常早期 ...

  4. uniapp实现钉钉扫码登录

    由于uniapp暂无钉钉授权登录所以本文将钉钉扫码登录作为网页嵌入uniapp,最终实现钉钉扫码登录app 1. 用H5调起钉钉扫码登录 钉钉在网页端的扫码登录可参考钉钉文档:扫码登录第三方网站 - ...

  5. tableau绘制热力地图

    一.右键国家地区和城市字段分别设置为地理角色-国家地区和城市 二.双击国家地区和城市添加到工作表 三.把订单id拖拽至标记卡的详细信息,标记改为密度显示,颜色设置为温度发散 四.最终整理结果如下图所示

  6. css预处理器和css Modules是干嘛的?

    CSS预处理器 1.css和js的区别 js是编程语言,它可以声明变量,编写逻辑.而css实际上只是个"表",表头是选择器,内容是里面的样式.它并不能写逻辑啥的.也就是说,对于cs ...

  7. HMAC在“挑战/响应”(Challenge/Response)身份认证的应用

    HMAC的一个典型应用是用在"挑战/响应"(Challenge/Response)身份认证中. 认证流程 (1) 先由客户端向服务器发出一个验证请求. (2) 服务器接到此请求后生 ...

  8. mysql的事务详解

    事务及其ACID属性 事务是由一组SQL语句组成的逻辑处理单元,事务具有以下4个属性,通常简称为事务的ACID属性. 原子性(Atomicity) :事务是一个原子操作单元,其对数据的修改,要么全都执 ...

  9. Kafka从入门到放弃(三)—— 详说消费者

    之前介绍了Kafka以及生产者,包括它的一些特性和参数,这回写一下消费者. 之前没看得可以点击链接阅读. Kafka从入门到放弃(一) -- 初识Kafka Kafka从入门到放弃(二) -- 详说生 ...

  10. vscode 设置

    { "security.workspace.trust.enabled": false, "workbench.editor.enablePreview": f ...