6682 Rikka with Mista

6682 Rikka with Mista

题意:给定n个元素,共有 2 n 2^n 2n个子集,求所有子集和中4的个数的和
分析:折半搜索+双指针查找+按位算贡献+桶排序



const int maxn = 40+1,maxm= 1048576+10;

LL f[10][maxm];// 桶排序
LL num[2][maxm];//
LL num1[2][maxm];
LL w[maxn];
int n,n1,n2;
void dfs(int x,int t,int n,LL val,int &cnt){
	if(x ==n+1){
		num[t][++cnt] = val;
		return ;
	}
	dfs(x+1,t,n,val,cnt);
	dfs(x+1,t,n,val+w[x],cnt);
}
bool cmp(const LL &a,const LL &b){
	return a%10 < b%10;
}
void Sort(LL *a,int n,LL t,int sign){
	// DEBUG;
	for(int i =0;i < 10; ++i)
		f[i][0] =0;
	for(int i = 1;i <= n; ++i){
		LL tmp = num[sign][i]%(t*10)/t;
		f[tmp][++f[tmp][0]] = num[sign][i];
	}
	// DEBUG;
	int tmp = 0;
	for(int i = 0;i <= 9; ++i){
		for(int j = 1;j <= f[i][0]; ++j){
			// cout<<t<<endl;
			// cout<<tmp<<endl;
			num[sign][++tmp] = f[i][j];
		}
	}
	// DEBUG;
	if(sign == 0)
		assert(tmp == n1);
	if(sign == 1)
		assert(tmp == n2);
}
int main(void)
{
	// freopen("test.in","r",stdin);
	int T;cin>>T;
	// cout<<T<<endl;
	while(T--){
		cin>>n;
		for(int i = 1;i <= n; ++i)
			scanf("%lld",&w[i]);

		dfs(1,0,n/2,0,n1=0);
		dfs(n/2+1,1,n,0,n2=0);
		assert(n1 == (1<<n/2));
		assert(n2 == (1<<(n-n/2)));
		// DEBUG;
		sort(num[0]+1,num[0]+n1+1,cmp);
		sort(num[1]+1,num[1]+n2+1,cmp);
		// DEBUG;
		LL t = 1;
		LL ans = 0;
		for(int i =0;i <= 9; ++i){
			
			LL t4 = 4*t;
			LL t5 = 5*t;
			LL t14 = t4+10*t;
			LL t15 = t5+10*t;
			// DEBUG;
			t *= 10;
			for(int j = 1;j <= n1; ++j)
				num1[0][j] = num[0][j]%t;
			for(int j = 1;j <= n2; ++j)
				num1[1][j] = num[1][j]%t;
			// for(int j = 1;j <= n1; ++j){
			// 	cout<<num1[1][j]<<" ";
			// }
			// cout<<endl;
			int l = n2+1,r =n2+1;
			num1[1][n2+1] = INFF;
			for(int j = 1;j <= n1; ++j){
				while(r >= 1&&num1[1][r] >= t5-num1[0][j]) r--;
				while(l >= 1&&num1[1][l] >= t4-num1[0][j]) l--;
				ans += r-l;
				// ans +=lower_bound(num1[1]+1,num1[1]+n2+1,t5-num1[0][j])- lower_bound(num1[1]+1,num1[1]+n2+1,t4-num1[0][j]);
				// ans +=lower_bound(num1[1]+1,num1[1]+n2+1,t15-num1[0][j])- lower_bound(num1[1]+1,num1[1]+n2+1,t14-num1[0][j]);

			}
			l = n2+1,r =n2+1;
			for(int j = 1;j <= n1; ++j){
				while(r >= 1&&num1[1][r] >= t15-num1[0][j]) r--;
				while(l >= 1&&num1[1][l] >= t14-num1[0][j]) l--;
				ans += r-l;
				// ans +=lower_bound(num1[1]+1,num1[1]+n2+1,t5-num1[0][j])- lower_bound(num1[1]+1,num1[1]+n2+1,t4-num1[0][j]);
				// ans +=lower_bound(num1[1]+1,num1[1]+n2+1,t15-num1[0][j])- lower_bound(num1[1]+1,num1[1]+n2+1,t14-num1[0][j]);

			}
			// cout<<ans<<endl;
			// DEBUG;
			Sort(num[0],n1,t,0);
			Sort(num[1],n2,t,1);
		}
		cout<<ans<<endl;
	}
    

   return 0;
}

/*
1
4
4 44 44 4444
4
444 44444 44444 4444444
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值