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权值线段树优化建边。

原文地址:https://www.cnblogs.com/Al-Ca/p/11629294.html