hdu1394(权值线段树求逆序数) Minimum Inversion Number
http://acm.hdu.edu.cn/showproblem.php?pid=1394
题目描述:
给定一个序列,每次左移一位,问得到的所有序列中最小的逆序数(序列中逆序对的数量)。
分析:用线段树维护数字出现的次数,每次插入一个树前,查询比他大的数个数,便是逆序数。
然后观察每次把开头移动到末尾后答案的变化。具体在代码解释:
代码:
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<sstream> #include<vector> #include<stack> #include<deque> #include<cmath> #include<map> #include<queue> #include<bitset> #include<hash_map> #define sd(x) scanf("%d",&x) #define ms(x,y) memset(x,y,sizeof x) #define fu(i,a,b) for(int i=a;i<=b;i++) #define fd(i,a,b) for(int i=a;i>=b;i--) #define all(a) a.begin(),a.end() #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 using namespace std; using namespace __gnu_cxx; typedef long long ll; typedef unsigned long long ull; typedef long double ld; const int maxn=5e4+79; const int mod=998244353; const int INF=1e9+7; const double pi=acos(-1); int t[maxn<<1],a[maxn]; void updata(int dis,int l,int r,int rt) { if(l==r) { //权值线段树,维护元素dis出现的次数 t[rt]++; return; } int mid=(l+r)>>1; if(dis<=mid) updata(dis,lson); else updata(dis,rson); t[rt]=t[rt<<1]+t[rt<<1|1]; } ll que(int L,int R,int l,int r,int rt) { if(l>=L&&r<=R) return t[rt]; int mid=(l+r)>>1; ll s=0; if(L<=mid) s+=que(L,R,lson); if(R>mid) s+=que(L,R,rson); return s; } int main() { int n; while(~sd(n)) { ms(t,0); ll ans=0; fu(i,1,n) { sd(a[i]);a[i]++;//变成1~n的排列 //找原来序列的逆序 ans+=que(a[i],n,1,n,1);//每次插入找比a[i]大的数个数,就是逆序数 updata(a[i],1,n,1);//插入该点 } ll res=ans; fu(i,1,n) { /* a[i]是枚举开头的数字,把他放末尾发生的变化如下: 1.之前他后面所有比他小的数字与他构成的逆序没了。 2.他后面所有比他大的数字在他到末尾后构成逆序。 */ res-=que(1,a[i],1,n,1)-1;//减1是把自己减出来 res+=que(a[i],n,1,n,1)-1; /* 因为这题是一个排列,所以所有数字都出现一次, 也可以直接减去,如下 res-=a[i]-1; res+=n-a[i]; */ ans=min(ans,res); } printf("%lld\n",ans); } return 0; }