atcoderAGC001 F

传送门:https://agc001.contest.atcoder.jp/tasks/agc001_f

s o l u t i o n solution solution
直接做肯定不好做,尝试转化一下模型
原本序列 a i {a_i} ai表示 i i i这个下标的值
新序列 b i b_i bi表示值为 i i i的下标

这有什么用呢?
原本的交换操作就变成相邻交换。

我们发现每个数字与后面和他绝对值之差<k的相对位置不会变了
可以归纳证明这个相对位置就足够充分的限制了最后的序列
然后我们就直接拓扑序来做就行了

但是边数可能很多怎么办?
对于对于 { b i } \{b_i\} {bi}中的第 i i i个元素,如果大于小于分开考虑,我们可以发现在一个限制里面,记 i i i的连到的集合为 s s s那么 s s s集合中两两都能产生限制。故 i i i只要向 s s s里面最靠前的元素连边即可。
线段树维护区间极值即可。

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back 
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
#define ls root*2
#define rs root*2+1
int rd()
{
    int sum = 0;char c = getchar();bool flag = true;
    while(c < '0' || c > '9') {if(c == '-') flag = false;c = getchar();}
    while(c >= '0' && c <= '9') sum = sum * 10 + c - 48,c = getchar();
    if(flag) return sum;
    else return -sum;
}
const int p = 1e9+7;
const int INF = 1e9;
int n,k;
int a[501000],b[501000],du[501000];
int ans[501000];
int tr[2001000];
int linkk[501000],t;
struct node{int n,y;}e[1001000];
void add(int root,int l,int r,int pl,int v)
{
    if(l == r) {tr[root] = v;return;}
    int mid = l+r>>1;
    if(pl <= mid) add(ls,l,mid,pl,v);
    else add(rs,mid+1,r,pl,v);
    tr[root] = min(tr[ls],tr[rs]);
}
void dele(int root,int l,int r,int pl)
{
    if(l == r) {tr[root] = INF;return;}
    int mid = l+r>>1;
    if(pl <= mid) dele(ls,l,mid,pl);
    else dele(rs,mid+1,r,pl);
    tr[root] = min(tr[ls],tr[rs]);
}
int ask(int root,int l,int r,int x,int y)
{
    if(l > y || r < x) return INF;
    if(x <= l && r <= y) return tr[root];
    int mid = l+r>>1;
    return min(ask(ls,l,mid,x,y),ask(rs,mid+1,r,x,y));
}
void insert(int x,int y)
{
    e[++t].y = y;e[t].n = linkk[x];linkk[x] = t;du[y]++;
}
priority_queue<int>q;
int main()
{
    n = rd();k = rd();rep(i,1,n) a[i] = rd(),b[a[i]] = i;
    memset(tr,10,sizeof(tr));
    if(k == 1)
    {
        rep(i,1,n) printf("%d\n",i);
        return 0;
    }
    rep(i,1,n) add(1,1,n,b[i],i);
    rep(i,1,n)
    {
        int t = ask(1,1,n,b[i]-k+1,b[i]-1);
        if(t > i && t <= n)insert(b[i],b[t]);
        t = ask(1,1,n,b[i]+1,b[i]+k);
        if(t > i && t <= n)insert(b[i],b[t]);
        dele(1,1,n,b[i]);
    }
    rep(i,1,n) if(du[i] == 0) q.push(-i);
    int tot = 0;
    while(!q.empty())
    {
        ans[-q.top()] = ++tot;
        int x = -q.top();q.pop();
        for(int i = linkk[x];i;i = e[i].n) 
        {
            du[e[i].y]--;
            if(du[e[i].y] == 0) q.push(-e[i].y);
        }
    }
    rep(i,1,n) printf("%d\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值