首先可以注意到对于序列
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
n≤B的情形,我们考虑直接解决这个背包问题。这里容易用折半搜索和哈希做到
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}}
a1≤2n−1q。考虑直接枚举
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(2nq⋅logq)的。
取
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;
}