[bzoj2058][Usaco2010 Nov]Cow Photographs_树状数组_动态规划
Cow Photographs bzoj-2058 Usaco-2010 Nov
题目大意:给定一个n的排列。每次操作可以交换相邻两个数。问将序列变成一个:$i,i+1,i+2,...,n,1,2,...,i-1$形式的序列最少操作次数。
注释:$1\le n\le 10^5$。
想法:
我们做过将序列变成1~n的形式,就是用树状数组求一下逆序对个数。
而这个题我们先求出i=1的答案,没错就是逆序对个数。
然后我们考虑如何从$i$变成$i+1$。
每次,我们考虑将当前序列中最小的数变成最大的数加1,再次求这个序列的逆序对个数,就是i+1的答案。
他们之间的关系,就是最小的数后面的数的个数减去前面的数的个数。
理由:
我们设f[i]为i开头的答案。
假设我们已经知道了f[i]的答案,即当前序列中的数是$i,i+1,i+2,..,n+i-1$。
这时我们将$i$变成$n+i$,$dic[i]$为i的位置。显然,dic[i]+1到n的数都比n+i小,而1到dic[i]-1的数都比i大,证毕。
最后,附上丑陋的代码... ...
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define N 100010 using namespace std; typedef long long ll; ll a[N],dic[N]; ll tree[N<<1]; ll n; inline char nc() {static char *p1,*p2,buf[100000]; return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;} ll rd() {ll x=0; char c=nc(); while(!isdigit(c)) c=nc(); while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=nc(); return x;} inline ll lowbit(ll x) {return x&(-x);} void update(ll x) {for(ll i=x;i;i-=lowbit(i)) tree[i]++;} ll query(ll x) {ll ans=0; for(ll i=x;i<=n+1;i+=lowbit(i)) ans+=tree[i]; return ans;} int main() { ll now=0,ans=0; n=rd(); for(int i=1;i<=n;i++) a[i]=rd(); for(ll i=1;i<=n;i++) { dic[a[i]]=i; now+=query(a[i]+1); update(a[i]); } ans=now; for(int i=1;i<n;i++) { now+=n+1-2*dic[i]; ans=min(ans,now); } printf("%lld\n",ans); return 0; }
小结:好题好题!!
| 欢迎来原网站坐坐! >原文链接<