题面:P5200 [USACO19JAN]Sleepy Cow Sorting
题解:
最小操作次数(记为k)即为将序列倒着找第一个P[i]>P[i+1]的下标,然后将序列分成三部分:前缀部分(待转移部分),k,后缀部分(不需转移部分)。
用树状数组维护前缀部分每一个数挪到后缀部分所需的最小代价(即插到第一个小于它的数前)(这部分完全可以用线段树做)。
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define ll long long 5 using namespace std; 6 const int maxn=(1e5)+50; 7 ll P[maxn],N,k,t[maxn]; 8 inline void Add(ll x){ 9 for(;x<=N;x+=x&(-x))t[x]++; 10 return; 11 } 12 inline ll Sum(ll x){ 13 ll sum=0; 14 for(;x>0;x-=x&(-x))sum+=t[x]; 15 return sum; 16 } 17 int main(){ 18 scanf("%lld",&N); 19 for(int i=1;i<=N;i++)scanf("%lld",&P[i]); 20 k=0;//若查询不到,k也应为0,所以先置为0 21 for(int i=N-1;i>=1;i--) 22 if(P[i]>P[i+1]){k=i; break;} 23 printf("%lld ",k); 24 if(k==0)return 0; 25 for(int i=k+1;i<=N;i++)Add(P[i]); 26 for(int i=1;i<=k;i++){ 27 printf("%lld ",k-i+Sum(P[i])); 28 Add(P[i]); 29 } 30 return 0; 31 }
By:AlenaNuna