• HZOJ Permutation


    输出原序列有45分……

    字典序最小可以和拓扑序联系起来。

    根据原来的题意不是很可做,于是对原序列求逆,令q[p[i]]=i;

    那么就成功将题意转化:相邻元素值的差大于等于k时可以交换,使序列字典序最小。

    考虑一下$n^2$怎么做,对于$i<j$,如果$abs(q[i]-q[j])<k$,那么q[i]和q[i]的大小关系不会发生变化,那么连一条$q[i]->q[j]$的边表示q[i]在q[j]之前,跑拓扑排序就可以了。

    考虑优化,其实做过很多这种题了(‘炸弹’),图中会存在$A->B,B->C,A->C$之类的边,显然$A->C$可以去掉。怎么实现呢?对于一个q[i],是向右边差小于k的连边,那么其实只需要连两条边,即离他最近的大于他的和小于他的,那么其它的点也会被他们限制。

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #include<cmath>
     5 #include<queue>
     6 #define LL long long
     7 using namespace std;
     8 struct edge
     9 {
    10     int u,v,nxt;
    11     #define u(x) ed[x].u
    12     #define v(x) ed[x].v
    13     #define n(x) ed[x].nxt
    14 }ed[10000010];
    15 int first[500010],num_e;
    16 #define f(x) first[x]
    17 int n,k,p[500010],q[500010],du[500010];
    18 struct TREE
    19 {
    20     struct tree
    21     {
    22         int l,r,ls,rs,minn;
    23         #define l(x) tr[x].l
    24         #define r(x) tr[x].r
    25         #define ls(x) tr[x].ls
    26         #define rs(x) tr[x].rs
    27         #define minn(x) tr[x].minn
    28     }tr[10000000];
    29     int cnt,root;
    30     void build(int &x,int l,int r)
    31     {
    32         if(!x)x=++cnt;
    33         l(x)=l,r(x)=r,minn(x)=0x7fffffff;
    34         if(l==r)return;
    35         int mid=(l+r)>>1;
    36         build(ls(x),l,mid);
    37         build(rs(x),mid+1,r);
    38     }
    39     void add(int x,int pos,int y)
    40     {
    41 //        cout<<x<<" "<<l(x)<<" "<<r(x)<<" "<<pos<<" "<<y<<endl;
    42         if(l(x)==r(x)){minn(x)=y;return;}
    43         int mid=(l(x)+r(x))>>1;
    44         if(pos<=mid)add(ls(x),pos,y);
    45         else         add(rs(x),pos,y);
    46         minn(x)=min(minn(ls(x)),minn(rs(x)));
    47     }
    48     int ask(int x,int l,int r)
    49     {
    50         if(l(x)>=l&&r(x)<=r)return minn(x);
    51         int mid=(l(x)+r(x))>>1,ans=0x7fffffff;
    52         if(l<=mid)ans=min(ans,ask(ls(x),l,r));
    53         if(r> mid)ans=min(ans,ask(rs(x),l,r));
    54         return ans;
    55     }
    56 }T;
    57 inline void add(int u,int v);
    58 signed main()
    59 {
    60     cin>>n>>k;T.build(T.root,1,n);
    61     for(int i=1;i<=n;i++)    
    62     cin>>p[i],q[p[i]]=i;
    63     
    64 //    for(int i=1;i<=n;i++)cout<<q[i]<<" ";puts("");
    65     for(int i=n;i;--i)
    66     {
    67         int t1=T.ask(1,max(1,q[i]-k+1),min(n,q[i]-1)),
    68             t2=T.ask(1,max(1,q[i]+1),min(n,q[i]+k-1));
    69 //        cout<<i<<" "<<t1<<" "<<t2<<" "<<q[i]<<endl;
    70         if(t1!=0x7fffffff)add(q[i],q[t1]),du[q[t1]]++;
    71         if(t2!=0x7fffffff)add(q[i],q[t2]),du[q[t2]]++;
    72         T.add(1,q[i],i);
    73     }
    74 
    75     priority_queue<int,vector<int>,greater<int> >que;
    76     for(int i=1;i<=n;i++)if(!du[i])que.push(i);
    77     int cnt=0;
    78     while(!que.empty())
    79     {
    80         int x=que.top();q[++cnt]=x;que.pop();
    81         for(int i=f(x);i;i=n(i))
    82         {
    83             du[v(i)]--;
    84             if(!du[v(i)])que.push(v(i));
    85         }
    86     }
    87     for(int i=1;i<=n;i++)p[q[i]]=i;
    88     
    89     for(int i=1;i<=n;i++)cout<<p[i]<<endl;
    90 }
    91 inline void add(int u,int v)
    92 {
    93 //    cout<<"add: "<<u<<" "<<v<<endl;
    94     ++num_e;
    95     u(num_e)=u;
    96     v(num_e)=v;
    97     n(num_e)=f(u);
    98     f(u)=num_e;
    99 }
    View Code

    %%mikufun权值线段树优化建边。

  • 相关阅读:
    【剑指Offer】49把字符串转换成整数
    【剑指Offer】48不用加减乘除做加法
    【剑指Offer】47求1+2+3+...+n
    判断两个线段是否相交
    EM算法--原理
    理解KMP算法
    阿里校招笔试的一道逻辑题
    线性拟合之最小二乘方法和最小距离方法
    Oracle Net Manager 服务命名配置以及用PL/SQL 登陆数据库
    正则表达式总结
  • 原文地址:https://www.cnblogs.com/Al-Ca/p/11629294.html
Copyright © 2020-2023  润新知