[折半搜索][分治][二分] Jzoj P5851 f
题解
- 考虑如何求逆序对,就可以转换为求01序列的逆序对,将当前位是0的放左边,当前位是1的放右边,分治下去
- 我们考虑给某个位置异或上一个1,那么当前位01取反
- 而前面的位置并没有改变,而且后面的分组也不会改变
- 把逆序对变成了顺序对
- 那么k<=30,直接跑为炸
- 考虑折半搜索,分别跑出前k/2的选法和后k/2的选法
- 然后可以二分逆序对的个数,记录下两个数的和小于等于mid的值
- 对于第二问,先算出ans1−1的选法个数
- 因为后面的是高位,我们暴力从小到大枚举后k/2位的选法
- 然后倍增求出有多少种选法使得和刚好为ans1,跑出第p大即可
代码
1 #include <cstdio> 2 #include <cmath> 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 const long long inf=30,maxn=5e5+10; 7 struct edge { long long x,num; }nl[maxn],nr[maxn]; 8 long long n,p,k,a[maxn],x[inf],y[inf],L[maxn],R[maxn],mi[maxn],mx,ans1,ans2,cnt1,cnt2; 9 void doit(long long d,long long l,long long r) 10 { 11 if ((d<0)||(l>=r)) return; 12 long long cnt1=0,cnt2=0; 13 for (long long i=l;i<=r;i++) 14 { 15 if (a[i]&mi[d]) 16 { 17 R[++cnt2]=a[i]; 18 y[d]+=(long long)cnt1; 19 } 20 else 21 { 22 L[++cnt1]=a[i]; 23 x[d]+=(long long)cnt2; 24 } 25 } 26 for (long long i=1;i<=cnt1;i++) a[l+i-1]=L[i]; 27 for (long long i=1;i<=cnt2;i++) a[l+cnt1+i-1]=R[i]; 28 doit(d-1,l,l+cnt1-1),doit(d-1,l+cnt1,r); 29 } 30 void dfs1(long long d,long long num,long long sum) 31 { 32 if (d>k/2) 33 { 34 nl[++cnt1]=(edge){sum,num}; 35 return; 36 } 37 dfs1(d+1,num,sum+x[d]),dfs1(d+1,num+mi[d],sum+y[d]); 38 } 39 void dfs2(long long d,long long num,long long sum) 40 { 41 if (d>=k) 42 { 43 nr[++cnt2]=(edge){sum,num}; 44 return; 45 } 46 dfs2(d+1,num,sum+x[d]),dfs2(d+1,num+mi[d],sum+y[d]); 47 } 48 bool cmp(edge a,edge b) { return a.x==b.x?a.num<b.num:a.x<b.x; } 49 bool cmp1(edge a,edge b) { return a.num<b.num; } 50 long long pd(long long k) 51 { 52 long long mx=0,l=1,r=cnt2; 53 while (l<=cnt1) 54 { 55 while (r>0&&nl[l].x+nr[r].x>k) r--; 56 mx+=r; 57 l++; 58 } 59 return mx; 60 } 61 long long get(long long k) 62 { 63 long long l=0,r=0; 64 for (long long i=(1<<15);i>0;i/=2) 65 if (l+i<=cnt1) 66 if (nl[l+i].x<k) l+=i; 67 for (long long i=(1<<15);i>0;i/=2) 68 if (r+i<=cnt1) 69 if (nl[r+i].x<=k) r+=i; 70 if (mx<=r-l) return nl[l+mx].num; 71 mx-=r-l; return -1; 72 } 73 int main() 74 { 75 freopen("f.in","r",stdin); 76 freopen("f.out","w",stdout); 77 scanf("%lld%lld%lld",&n,&k,&p); 78 for (long long i=1;i<=n;i++) scanf("%lld",&a[i]); 79 mi[0]=1; for (long long i=1;i<inf;i++) mi[i]=mi[i-1]*2; 80 doit(inf-1,1,n); 81 dfs1(0,0,0),dfs2(k/2+1,0,0); 82 sort(nl+1,nl+cnt1+1,cmp),sort(nr+1,nr+cnt2+1,cmp); 83 long long l=0,r=n*(n-1)>>1; 84 while (l<=r) 85 { 86 long long mid=(l+r)>>1; 87 if ((pd(mid))>=p) r=mid-1,ans1=mid; else l=mid+1; 88 } 89 mx=p-pd(ans1-1); 90 sort(nr+1,nr+cnt2+1,cmp1); 91 for (long long i=1;i<=cnt2;i++) 92 { 93 long long w=get(ans1-nr[i].x); 94 if (w!=-1) 95 { 96 ans2=w+nr[i].num; 97 break; 98 } 99 } 100 printf("%lld %lld",ans1,ans2); 101 return 0; 102 }