题目链接: 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 (线段树、拓扑排序)的更多相关文章

  1. 【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 ...

  2. BZOJ4383 Pustynia(线段树+拓扑排序)

    线段树优化建图暴力拓扑排序即可.对于已确定的数,拓扑排序时dp,每个节点都尽量取最大值,如果仍与已确定值矛盾则无解.叶子连出的边表示大于号,其余边表示大于等于. #include<iostrea ...

  3. hdu 5195 DZY Loves Topological Sorting 线段树+拓扑排序

    DZY Loves Topological Sorting Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/sho ...

  4. HDU5638 / BestCoder Round #74 (div.1) 1003 Toposort 线段树+拓扑排序

    Toposort   问题描述 给出nn个点mm条边的有向无环图. 要求删掉恰好kk条边使得字典序最小的拓扑序列尽可能小. 输入描述 输入包含多组数据. 第一行有一个整数TT, 表示测试数据组数. 对 ...

  5. [CSP-S模拟测试]:Permutation(线段树+拓扑排序+贪心)

    题目描述 你有一个长度为$n$的排列$P$与一个正整数$K$你可以进行如下操作若干次使得排列的字典序尽量小对于两个满足$|i−j|\geqslant K$且$|P_i−P_j|=1$的下标$i$与$j ...

  6. BZOJ3832[Poi2014]Rally——权值线段树+拓扑排序

    题目描述 An annual bicycle rally will soon begin in Byteburg. The bikers of Byteburg are natural long di ...

  7. BZOJ_3012_[Usaco2012 Dec]First!_trie树+拓扑排序

    BZOJ_3012_[Usaco2012 Dec]First!_trie树+拓扑排序 题意: 给定n个总长不超过m的互不相同的字符串,现在你可以任意指定字符之间的大小关系.问有多少个串可能成为字典序最 ...

  8. bzoj3276磁力 两种要求下的最大值:分块or线段树+拓扑

    进阶指南上的做法是分块的.. 但是线段树搞起来也挺快,将磁石按照距离排序,建立线段树,结点维护区间质量最小值的下标 进行拓扑,每次在可行的范围内在线段树中找到质量最小的下标取出,取出后再将线段树对应的 ...

  9. Codeforces 588E. A Simple Task (线段树+计数排序思想)

    题目链接:http://codeforces.com/contest/558/problem/E 题意:有一串字符串,有两个操作:1操作是将l到r的字符串升序排序,0操作是降序排序. 题解:建立26棵 ...

随机推荐

  1. 数据库中的round()

    Round函数返回一个数值,该数值是按照指定的小数位数进行四舍五入运算的结果.可是当保留位跟着的即使是5,有可能进位,也有可能舍去,机会各50%.这样就会造成在应用程序中计算有误. 参数规范 语法 r ...

  2. luoguP4578_ [FJOI2018]所罗门王的宝藏

    题意 一个n*m的矩阵,初始值全为0,每一行每一列操作一次可以加1或者减1,问能否操作得到给定矩阵. 分析 行和列的分别的加减是可以相互抵消的,因此我们只需要考虑行的加和列的减. 对于给定矩阵每一个数 ...

  3. [转载]C / C++ 计算程序运行的时间

    原文链接:https://blog.csdn.net/qq_36667170/article/details/79507547 在学数据结构过程中老师让查看不同算法的运行时间,然后让自己打印运行时间. ...

  4. springboot中的编码设置

    在springboot中编码配置可以通过filter也可以通过springboot的核心配置文件application.properties中配置如下信息: #配置字符编码spring.http.en ...

  5. spring boot引入thymeleaf导致中文乱码

    加上下面这句代码 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" / ...

  6. 通过SEO可以做什么?

    1.做关键词排名,从而带来大量的客户卖产品和服务.很多大型机械厂家就是这么做的,显然SEO赚取了不少利润.     2.通过SEO带来大量流量,从而投放广告或者跟厂家合作买产品,比如流量站之类.    ...

  7. Java学习路线(完整详细版)

    Java学习路线(完整详细版) https://jingyan.baidu.com/article/c1a3101e110864de656deb83.html

  8. 在docker容器中为elasticsearch配置跨域访问

    一.在docker容器中进入elasticsearch对应的容器 docker exec -it [容器名] /bin/bash 二.安装vim编辑器 因为我们需要更改配置文件,安装过的朋友就不用安装 ...

  9. WebService简单使用教程

    根据说明书获取信息 代码示例: import com.gyf.weather.ws.ArrayOfString; import com.gyf.weather.ws.WeatherWS; import ...

  10. oracle使用between and边界问题

    如果是只查询某两个月之间的数据,会默认从每个月的1号开始查,包前包后 eg:select * from test_hsj where regdate between to_date('2015-05' ...