AtCoder AGC001F Wide Swap (线段树、拓扑排序)
题目链接: https://atcoder.jp/contests/agc001/tasks/agc001_f
题解: 先变成排列的逆,要求\(1\)的位置最小,其次\(2\)的位置最小,依次排下去(称之为逆字典序)。有一些条件,如果两数\(x,y\)的差小于\(K\), 那么它们的相对位置不可变。
所以如果从必须在前面的往必须在后面的连边,得到的图将是一个DAG,现在需要求它的一个拓扑序满足上面的最优化条件。
先排除几个错误结论: 翻转后字典序越大,字典序越小,错误。逆字典序越大,字典序越大/越小,错误。
有这样一个正确结论: 逆字典序最小的拓扑序即为翻转后字典序最大的拓扑序。注意必须是在一个图的拓扑序的集合中,不可拓展到任意排列的集合中,而且是“最大”“最小”,不可拓展为“越大”“越小”。
对于这个结论,网上好多感性理解/证明都是明显有问题的。我给出一个我自己的证明: (可能有错,有错请指出) 考虑归纳,假设往图里添加一个新的点\(n\), 假设在翻转后字典序最大的拓扑序里\(n\)的位置为\(k\), 那么\(n\)一定要向位置\((k+1)\)上的数连边(否则交换它们会使得翻转后字典序更大),即\(n\)现在所处的位置是合法情况下其所处的最靠后的位置。又因为在没加\(n\)之前该排列是逆字典序最小的,因此加了\(n\)之后会使得后面最小个数的小于\(n\)的数位置后移\(1\), 因此加了之后依然是最小。
所以我们只需要建出图来求翻转后字典序最小的拓扑序,然而边数是\(O(n^2)\)的,但是发现连边时只需要考虑区间内最小的和最大的即可。线段树优化。
时间复杂度\(O(n\log n)\)
代码
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<utility>
#include<algorithm>
#include<queue>
#define llong long long
using namespace std;
const int N = 5e5;
struct SegmentTree
{
struct SgTNode
{
int val;
} sgt[(N<<2)+2];
void pushup(int pos) {sgt[pos].val = max(sgt[pos<<1].val,sgt[pos<<1|1].val);}
void modify(int pos,int le,int ri,int lrb,int val)
{
if(le==lrb && ri==lrb) {sgt[pos].val = val; return;}
int mid = (le+ri)>>1;
if(lrb<=mid) {modify(pos<<1,le,mid,lrb,val);}
else if(lrb>mid) {modify(pos<<1|1,mid+1,ri,lrb,val);}
pushup(pos);
}
int querymax(int pos,int le,int ri,int lb,int rb)
{
if(lb<=le && rb>=ri) {return sgt[pos].val;}
int mid = (le+ri)>>1;
int ret = 0;
if(rb>mid) {ret = max(ret,querymax(pos<<1|1,mid+1,ri,lb,rb));}
if(lb<=mid) {ret = max(ret,querymax(pos<<1,le,mid,lb,rb));}
return ret;
}
} smt;
struct Edge
{
int v,nxt;
} e[(N<<1)+2];
int a[N+3];
int b[N+3];
int ind[N+3];
int fe[N+3];
priority_queue<pair<int,int> > pq;
pair<int,int> ans[N+3];
int fans[N+3];
int n,m,en;
void addedge(int u,int v)
{
// printf("addedge%d %d\n",u,v);
en++; e[en].v = v; ind[v]++;
e[en].nxt = fe[u]; fe[u] = en;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) {int x; scanf("%d",&b[i]); a[b[i]] = i;}
for(int i=1; i<=n; i++)
{
ans[i].second = a[i];
int x = smt.querymax(1,0,n,max(a[i]-m+1,0),a[i]);
int y = smt.querymax(1,0,n,a[i],min(a[i]+m-1,n));
if(x) {addedge(i,x);}
if(y) {addedge(i,y);}
// printf("iquery %d %d %d\n",i,max(0,a[i]-m+1),min(n,a[i]+m-1));
// printf("imodify %d %d %d\n",i,ans[i].second,ans[i].first);
smt.modify(1,0,n,ans[i].second,i);
// printf("ans%d %d %d\n",i,ans[i].first,ans[i].second);
}
for(int i=1; i<=n; i++) if(ind[i]==0) {pq.push(make_pair(a[i],i));}
int j = 0;
while(!pq.empty())
{
pair<int,int> tmp = pq.top(); pq.pop();
j++; b[j] = tmp.first; int u = tmp.second;
for(int i=fe[u]; i; i=e[i].nxt)
{
ind[e[i].v]--;
if(ind[e[i].v]==0)
{
pq.push(make_pair(a[e[i].v],e[i].v));
}
}
}
for(int i=1; i<n+1-i; i++) swap(b[i],b[n+1-i]);
for(int i=1; i<=n; i++) fans[b[i]] = i;
for(int i=1; i<=n; i++) printf("%d\n",fans[i]);
return 0;
}
AtCoder AGC001F Wide Swap (线段树、拓扑排序)的更多相关文章
- 【AtCoder Grand Contest 001F】Wide Swap [线段树][拓扑]
Wide Swap Time Limit: 50 Sec Memory Limit: 512 MB Description Input Output Sample Input 8 3 4 5 7 8 ...
- BZOJ4383 Pustynia(线段树+拓扑排序)
线段树优化建图暴力拓扑排序即可.对于已确定的数,拓扑排序时dp,每个节点都尽量取最大值,如果仍与已确定值矛盾则无解.叶子连出的边表示大于号,其余边表示大于等于. #include<iostrea ...
- hdu 5195 DZY Loves Topological Sorting 线段树+拓扑排序
DZY Loves Topological Sorting Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/sho ...
- HDU5638 / BestCoder Round #74 (div.1) 1003 Toposort 线段树+拓扑排序
Toposort 问题描述 给出nn个点mm条边的有向无环图. 要求删掉恰好kk条边使得字典序最小的拓扑序列尽可能小. 输入描述 输入包含多组数据. 第一行有一个整数TT, 表示测试数据组数. 对 ...
- [CSP-S模拟测试]:Permutation(线段树+拓扑排序+贪心)
题目描述 你有一个长度为$n$的排列$P$与一个正整数$K$你可以进行如下操作若干次使得排列的字典序尽量小对于两个满足$|i−j|\geqslant K$且$|P_i−P_j|=1$的下标$i$与$j ...
- BZOJ3832[Poi2014]Rally——权值线段树+拓扑排序
题目描述 An annual bicycle rally will soon begin in Byteburg. The bikers of Byteburg are natural long di ...
- BZOJ_3012_[Usaco2012 Dec]First!_trie树+拓扑排序
BZOJ_3012_[Usaco2012 Dec]First!_trie树+拓扑排序 题意: 给定n个总长不超过m的互不相同的字符串,现在你可以任意指定字符之间的大小关系.问有多少个串可能成为字典序最 ...
- bzoj3276磁力 两种要求下的最大值:分块or线段树+拓扑
进阶指南上的做法是分块的.. 但是线段树搞起来也挺快,将磁石按照距离排序,建立线段树,结点维护区间质量最小值的下标 进行拓扑,每次在可行的范围内在线段树中找到质量最小的下标取出,取出后再将线段树对应的 ...
- Codeforces 588E. A Simple Task (线段树+计数排序思想)
题目链接:http://codeforces.com/contest/558/problem/E 题意:有一串字符串,有两个操作:1操作是将l到r的字符串升序排序,0操作是降序排序. 题解:建立26棵 ...
随机推荐
- Spring依赖模块
1.spring maven依赖 https://www.cnblogs.com/nwu-edu/p/9542074.html
- Go语言基本数据类型(四)
Go语言基本数据类型主要包含:bool,number,string 布尔类型 go中的布尔类型,只能是 true 和 false,例如:var b bool = true 数字类型 整型 类型 描述 ...
- Python 入门之 内置模块 -- datetime模块
Python 入门之 内置模块 -- datetime模块 1.datetime模块 from datetime import datetime (1)datetime.now() 获取当前时间和日期 ...
- AtCoder Beginner Contest 072
这应该是我第二次打AtCoder, 题目其实并不难,就是自己经验不足想复杂了,再加上自己很笨,愣是做了97分钟才全做出来(最后三分钟,有点小激动..),看着前面大牛半个小时都搞完了,真心膜拜一下,代码 ...
- php前台表单限制PHP上传大小
在php文件上传时候,一般我都认为考虑php.ini配置修改文件上传大小,还后台控制上传大小,这里教你php前台表单限制PHP上传大小 <form action="http://www ...
- 掌握 analyze API,一举搞定 Elasticsearch 分词难题
初次接触 Elasticsearch 的同学经常会遇到分词相关的难题,比如如下这些场景: 为什么明明有包含搜索关键词的文档,但结果里面就没有相关文档呢? 我存进去的文档到底被分成哪些词(term)了? ...
- 使用layer弹窗和layui表单做新增功能
注释:代码参考http://blog.51cto.com/825272560/1891158,在其修改之上而来,在此感谢! 1.需求:使用layer在弹窗内完成新增,成功后提示并刷新页面(父页面,li ...
- vue项目--vuex状态管理器
本文取之官网和其他文章结合自己的理解用简单化的语言表达.用于自己的笔记记录,也希望能帮到其他小伙伴理解,学习更多的前端知识. Vuex 是什么? Vuex 是一个专为 Vue.js 应用程序开发的状态 ...
- [转载]MII/MDIO接口详解
原文地址:MII/MDIO接口详解作者:心田麦浪 本文主要分析MII/RMII/SMII,以及GMII/RGMII/SGMII接口的信号定义,及相关知识,同时本文也对RJ-45接口进行了总结,分析了在 ...
- flex布局总结回顾
1.背景 传统css盒式模型,依赖 display属性 + position属性 + float属性实现页面的布局,而随着互联网的迅猛发展,带动了无数的互联网创业者和互联网产品,因而样式布局的美化成为 ...