0
点赞
收藏
分享

微信扫一扫

2019HDU多校赛 第九场 HDU 6682 Rikka with Mista(折半搜索 + 组合计数 + 排序)


2019HDU多校赛 第九场 HDU 6682 Rikka with Mista(折半搜索 + 组合计数 + 排序)_2019HDU多校赛

2019HDU多校赛 第九场 HDU 6682 Rikka with Mista(折半搜索 + 组合计数 + 排序)_折半搜索_02

 

 

大致题意:给你最多40个数字,你可以任意的取数字,问所有的取法下,所有取的数字的和中4的个数的和是多少。

40个数字,其实就是折半搜索,但是好像有一个听起来好像挺厉害的名字meet in middle。具体来说,数字分为两半,然后分别求出两部分可以构成的所有的和。这样两部分分别最多有2^20约100W种数字,然后我们考虑这两部分求和。

由于是计算和种4出现的次数,所以我们考虑按位来统计。对于某一位i,我们考虑对两部分所有可能的子集和按照后i位排序,也即模10^(i+1)排序。然后,考虑如果第i位为4,那么满足的条件就是第一部分的某个数字加上第二部分的某个数字的和模10^(i+1)一定介于4*10^i和5*10^i之间。由于我们已经排了序,所以对于第一部分的某个数字,他可行的的数字一定是两个连续的区间(分别对应和会进位和不进位)。而且,随着第一部分的数字越来越大,这两个区间是单调的,所以这个过程我们可以用指针模拟,总的复杂度就是O(其中一部分子集个数)也即O(2^(n/2))。

我们考虑一下目前为止的复杂度,由于是按位统计,所以是有最多10位,然后每位需要进行一个排序和统计,总的复杂度就是O(10*2^(n/2)log(2^(n/2))),直接这么做是会超时的。可以看到瓶颈在于排序。这里为了优化排序,我们考虑另外有两种排序方法可以优化。

首先是归并排序,我们考虑从高位开始往下,首先按照模10^10的值,这个没办法只能直接排序。然后到下一位的时候,是模10^9排序。你会发现,对于最高位相同的数字来说,他们已经相当于是按照模10^9排了序,而最高为最多只有10种取法。那么这就意味着,这百万个数字,可以分为最多10段,每一段内都是已经按照10^9排好序了的。那么,这正好就可以利用归并排序,把这最多10个有序段合并成一个,复杂度就是O(10*10*2^(n/2)))。

另一种是归并排序,这次考虑从小的开始做。初始时相当于已经按照最低为的下一位(0)排好序,现在多了一位,我们按照原本的排列顺序,从前往后,如果下一位是1的,放到1的位置,2的放到2的位置,以此类推,最多有10个位置。然后从0开始到9,按照先入先出的顺序一个个取出来,当前的顺序就是按照模下一位排序的顺序。为什么呢?按照0到9的顺序出是因为最高位的比较,而按照先入先出则是考虑了之前的排序。这个思想有点像后缀数组的思想了。如此复杂度会比归并小一些,复杂度是O(10*2^(n/2))),但是由于需要知道某一位是多少,所以要用上取模和整除运算,加上常数实际复杂度二者相近。

具体见代码:

归并排序:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define eps 1e-4
#define pi 3.141592653589793
#define P 1000000007
#define LL long long
#define LDB long double
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<" : "<<x<<endl
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%lld%lld",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;

const int N = 1<<20;

int a[N],A[N],B[N],C[N],t1,t2,n,m;
int pw[10],L[10],R[10];

inline void MergeSort(int *A,int t,int i)
{
int tt=0; L[0]=0;
for(int j=0;j<t;j++)
if (j&&A[j]/pw[i]!=A[j-1]/pw[i]) R[tt++]=j-1,L[tt]=j;
for(int j=0;j<t;j++) A[j]%=pw[i]; R[tt++]=t-1;
if (tt==1) return;
for(int j=0;j<t;j++)
{
int l=2e9,pos;
for(int k=0;k<tt;k++)
if (L[k]<=R[k]&&A[L[k]]<l) l=A[L[pos=k]];
C[j]=l; L[pos]++;
}
for(int j=0;j<t;j++) A[j]=C[j];
}

int main()
{
int T; sc(T);
pw[0]=1;
for(int i=1;i<10;i++)
pw[i]=pw[i-1]*10;
while(T--)
{
sc(n);
for(int i=0;i<n;i++) sc(a[i]);
m=n/2; n=n-n/2; t1=t2=0;
int up1=1<<m,up2=1<<n;
for(int i=0;i<up1;i++)
{
int sum1=0,sum2=0;
for(int j=0;j<m;j++)
if ((i>>j)&1) sum1+=a[j],sum2+=a[j+m];
A[t1++]=sum1; B[t2++]=sum2;
}
for(int i=up1;i<up2;i++)
{
int sum=0;
for(int j=0;j<n;j++)
if ((i>>j)&1) sum+=a[j+m];
B[t2++]=sum;
}
LL ans=0;
sort(A,A+t1); sort(B,B+t2);
// puts("sdf");
for(int i=9;i;i--)
{
int up=5*pw[i-1],low=4*pw[i-1];
for(int j=0,k=t2-1,l=t2-1,kk=t2-1,ll=t2-1;j<t1;j++)
{
while(~k&&A[j]+B[k]>=up) k--;
while(~l&&A[j]+B[l]>=low) l--;
while(~kk&&A[j]+B[kk]>=up+pw[i]) kk--;
while(~ll&&A[j]+B[ll]>=low+pw[i]) ll--;
ans+=k-l+kk-ll;
}
if (i==1) break;
MergeSort(A,t1,i-1);
MergeSort(B,t2,i-1);
}
printf("%lld\n",ans);
}
return 0;
}

基数排序:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define eps 1e-4
#define pi 3.141592653589793
#define P 1000000007
#define LL long long
#define LDB long double
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<" : "<<x<<endl
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%lld%lld",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;

const int N = 1<<20;

int a[N],A[N],B[N],t1,t2,n,m;
vector<int> pos[10];
int pw[10];

inline void Sort(int *A,int t,int i)
{
memset(pos,0,sizeof(pos));
for(int j=0;j<t;j++)
pos[A[j]/pw[i]%10].pb(A[j]);
t=0;
for(int i=0;i<10;i++)
for(int j=0;j<pos[i].size();j++)
A[t++]=pos[i][j];
}

int main()
{
int T; sc(T);
pw[0]=1;
for(int i=1;i<10;i++)
pw[i]=pw[i-1]*10;
while(T--)
{
sc(n);
for(int i=0;i<n;i++) sc(a[i]);
m=n/2; n=n-n/2; t1=t2=0;
int up1=1<<m,up2=1<<n;
for(int i=0;i<up1;i++)
{
int sum1=0,sum2=0;
for(int j=0;j<m;j++)
if ((i>>j)&1) sum1+=a[j],sum2+=a[j+m];
A[t1++]=sum1; B[t2++]=sum2;
}
for(int i=up1;i<up2;i++)
{
int sum=0;
for(int j=0;j<n;j++)
if ((i>>j)&1) sum+=a[j+m];
B[t2++]=sum;
}
LL ans=0;
// puts("sdf");
for(int i=1;i<=9;i++)
{
Sort(A,t1,i-1); Sort(B,t2,i-1);
int up=5*pw[i-1],low=4*pw[i-1];
int upp=up+pw[i],loww=low+pw[i];
for(int j=0,k=t2-1,l=t2-1,kk=t2-1,ll=t2-1;j<t1;j++)
{
while(~k&&A[j]%pw[i]+B[k]%pw[i]>=up) k--;
while(~l&&A[j]%pw[i]+B[l]%pw[i]>=low) l--;
while(~kk&&A[j]%pw[i]+B[kk]%pw[i]>=upp) kk--;
while(~ll&&A[j]%pw[i]+B[ll]%pw[i]>=loww) ll--;
ans+=k-l+kk-ll;
}
}
printf("%lld\n",ans);
}
return 0;
}

 

举报

相关推荐

0 条评论