题意:
一共有n份资料,每天随机选一个区间[l,r],Mato按文件从小到大的顺序看编号在此区间内的这些资料。他先把要看的文件按编号顺序依次拷贝出来,再用排序程序给文件大小排序。求每天排序时的交换次数。
题解:
还是莫队,但是转移的时候用树状数组维护逆序对个数,总复杂度为O(nsqrt(n)log2n)。因为是从大到小插入的,所以维护时要用r-l+1减。
代码:
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <cmath> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 #define lowbit(a) a&(-a) 7 #define ll long long 8 using namespace std; 9 10 struct nd1{int x,y;}; 11 bool cmp1(nd1 a,nd1 b){return a.x<b.x;} 12 struct nd2{int l,pl,r; ll ans; int id;}; 13 bool cmp2(nd2 a,nd2 b){if(a.pl!=b.pl)return a.pl<b.pl; if(a.r!=b.r)return a.r<b.r; return a.l<b.l;} 14 bool cmp3(nd2 a,nd2 b){return a.id<b.id;} 15 ll c[100000],ans,l,r; int n,q,ls[100000],pos[100000]; nd1 f[100000]; nd2 ask[100000]; 16 inline void add(int x,ll y){while(x<=n)c[x]+=y,x+=lowbit(x);} 17 inline ll query(int x){ll qy=0; while(x>=1)qy+=c[x],x-=lowbit(x); return qy;} 18 int main(){ 19 scanf("%d",&n); inc(i,1,n)scanf("%d",&f[i].x),f[i].y=i; sort(f+1,f+n+1,cmp1); 20 inc(i,1,n)ls[f[i].y]=i; int sz=(int)sqrt(n); inc(i,1,n)pos[i]=(i-1)/sz+1; 21 scanf("%d",&q); inc(i,1,q){int a,b; scanf("%d%d",&a,&b); ask[i]=(nd2){a,pos[a],b,0,i};} 22 sort(ask+1,ask+1+q,cmp2); memset(c,0,sizeof(c)); ans=0; l=1; r=0; 23 inc(i,1,q){ 24 while(r<ask[i].r){int x=r+1; x=n-ls[x]; ll a1=query(x); ans+=a1; add(x+1,1); r++;} 25 while(l>ask[i].l){int x=l-1; x=n-ls[x]; ll a1=(r-l+1)-query(x); ans+=a1; add(x+1,1); l--;} 26 while(r>ask[i].r){int x=r; x=n-ls[x]; ll a1=query(x); ans-=a1; add(x+1,-1); r--;} 27 while(l<ask[i].l){int x=l; x=n-ls[x]; ll a1=(r-l)-query(x); ans-=a1; add(x+1,-1); l++;} 28 ask[i].ans=ans; 29 } 30 sort(ask+1,ask+1+q,cmp3); inc(i,1,q)printf("%lld ",ask[i].ans); 31 return 0; 32 }
20160408