Codeforces gym 101630 K

100 篇文章 0 订阅
2 篇文章 0 订阅

首先可以注意到对于序列 a i a_i ai,我们可以倒着贪心来线性求解,这也说明对于任意的 s   m o d   q s\bmod q smodq,要么无解要么有唯一解。而对于序列整体乘一个 gcd ⁡ ( r , q ) = 1 \gcd(r,q)=1 gcd(r,q)=1的数字 r r r,显然不改变解的存在性和唯一性。于是,我们说明了我们可以对序列整体乘一个 r r r来求解。
考虑取一个阈值 B B B,对 n n n按大小分别处理。
对于 n ≤ B n\leq B nB的情形,我们考虑直接解决这个背包问题。这里容易用折半搜索和哈希做到 O ( 2 n 2 ) \mathcal O(2^{\frac{n}{2}}) O(22n)
对于 n > B n>B n>B的情形,可以发现这时候原来的序列 a i a_i ai一定有 a 1 ≤ q 2 n − 1 a_1\leq \frac{q}{2^{n-1}} a12n1q。考虑直接枚举 a 1 a_1 a1的值反解出 r r r,然后验证求得的 a a a序列是否合法(注意可能不唯一),如果找到了某个合法的 a a a序列,就可以直接贪心求解了。这里需要满足 v 2 ( a 1 ) = v 2 ( b 1 ) v_2(a_1)=v_2(b_1) v2(a1)=v2(b1),同时还发现对于某个固定的 a 1 a_1 a1,对应的 r r r也不是唯一的(有 2 v 2 ( b 1 ) 2^{v_2(b_1)} 2v2(b1)个),但由于这时候 a 1 a_1 a1必定是 2 v 2 ( b 1 ) 2^{v_2(b_1)} 2v2(b1)的倍数,因此不会影响复杂度。这部分复杂度是 O ( q 2 n ⋅ log ⁡ q ) \mathcal O(\frac{q}{2^n}\cdot \log q) O(2nqlogq)的。
B = O ~ ( 2 3 log ⁡ 2 q ) B=\mathcal {\tilde{O}}(\frac{2}{3}\log_2q) B=O~(32log2q)左右达到最优复杂度 O ~ ( q 1 3 ) \mathcal {\tilde{O}}(q^{\frac{1}{3}}) O~(q31)

#include <bits/stdc++.h>
#define lowbit(x) (x&-x)

using namespace std;

typedef unsigned long long ull;

ull pow_mod(ull x,ull k) {
  ull ans=1;
  while (k) {
  	if (k&1) ans*=x;
  	x*=x;
  	k>>=1;
  }
  return ans;
}

namespace Hash {

const int MOD=10000007; 

ull num[(1<<23)+5];
int val[(1<<23)+5],nxt[(1<<23)+5],head[MOD],tot;

void insert(ull x,int y) {
  int v=x%MOD;
  num[++tot]=x;
  val[tot]=y;
  nxt[tot]=head[v];
  head[v]=tot;
}

int find(ull x) {
  int v=x%MOD;
  for(int i=head[v];i;i=nxt[i])
    if (num[i]==x) return val[i];
  return -1;
}

}

ull num[65],sumv[1<<23],val[1<<23];

void solve1(int n,ull m) {
  int lb=(n>>1),rb=n-lb;
  for(int i=1;i<=lb;i++) val[1<<(i-1)]=num[i];
  for(int i=0;i<(1<<lb);i++) {
  	sumv[i]=sumv[i^lowbit(i)]+val[lowbit(i)];
  	Hash::insert(sumv[i],i);
  }
  for(int i=1;i<=rb;i++) val[1<<(i-1)]=num[lb+i];
  for(int i=0;i<(1<<rb);i++) {
  	sumv[i]=sumv[i^lowbit(i)]+val[lowbit(i)];
  	int t=Hash::find(m-sumv[i]);
  	if (t!=-1) {
  		for(int j=1;j<=lb;j++) putchar('0'+((t>>(j-1))&1));
  		for(int j=1;j<=rb;j++) putchar('0'+((i>>(j-1))&1));
  		printf("\n");
  		return;
	  }
  }
} 

const ull M=18446744073709551615uLL;
ull cur[65];
bool ans[65];

bool check(int n,ull m,ull r) {
  for(int i=1;i<=n;i++) cur[i]=num[i]*r;
  ull s=0;
  for(int i=1;i<n;i++) {
  	if (s>=cur[i+1]||cur[i]>=cur[i+1]-s) return 0;
	s+=cur[i];
  }
  if (cur[n]>M-s) return 0;
  m=m*r;
  for(int i=n;i>0;i--)
	if (m>=cur[i]) {
	  m-=cur[i];
	  ans[i]=1;
	}
  assert(!m);
  return 1;
}

void solve2(int n,ull m) {
  ull x=num[1];
  int t=0;
  while (x%2==0) {
  	x/=2;
  	t++;
  }
  ull v=(1uLL<<t);
  for(;;) {
  	ull rv=pow_mod((v>>t),(1uLL<<(63-t))-1)*(num[1]>>t);
  	assert(v*rv==num[1]);
  	for(ull i=0;i<(1<<t);i++) {
  		if (check(n,m,pow_mod(rv,(1uLL<<63)-1))) {
  			for(int j=1;j<=n;j++) putchar(ans[j]+'0');
  			printf("\n");
  			return;
		  }
		rv+=(1uLL<<(64-t));
	  }
  	v+=(1uLL<<(t+1));
  }
}

int main() {
  int n;
  scanf("%d",&n);
  for(int i=1;i<=n;i++) scanf("%llu",&num[i]);
  ull m;
  scanf("%llu",&m);
  if (n<=46) solve1(n,m);
  else solve2(n,m);
  return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值