[折半搜索][分治][二分] Jzoj P5851 f

Description

 

Input

Output

 

Sample Input

见下发文件

Sample Output

见下发文件
 

Data Constraint

 

Hint

 

 

题解

  • 考虑如何求逆序对,就可以转换为求01序列的逆序对,将当前位是0的放左边,当前位是1的放右边,分治下去
  • 我们考虑给某个位置异或上一个1,那么当前位01取反
  • 而前面的位置并没有改变,而且后面的分组也不会改变
  • 把逆序对变成了顺序对
  • 那么k<=30,直接跑为炸
  • 考虑折半搜索,分别跑出前k/2的选法和后k/2的选法
  • 然后可以二分逆序对的个数,记录下两个数的和小于等于mid的值
  • 对于第二问,先算出ans11的选法个数
  • 因为后面的是高位,我们暴力从小到大枚举后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 }

 

posted @ 2018-09-14 21:05  BEYang_Z  阅读(172)  评论(0编辑  收藏  举报