背包密码系统是一种非常经典的公钥密码系统,这种密码系统加密过程如下:
- 选取一个长度为n的正整数超递增序列a[i],满足a[1]<a[2],a[1]+a[2]<a[3],……,a[1]+a[2]+…+a[n-1]<a[n]。
- 选取正整数m>2*a[n],w和m互质,v是w模m的逆,即(v*w)%m=1。b[i]=(w*a[i])%m。
- 加密时,对一段长为n的二进制串x[i]。有S= (b[1]*x[1]+b[2]*x[2]+…+b[n]*x[n])%m。
现在告诉你b[i]、v和m以及S,请你帮忙进行解密。
v是w的逆,所以可以求解出a[i] = (b[i] * v) % m.
因为sum = ∑a[i](i = 1~n-1) < a[n],所以当a[i] <= sum时,a[i] += ((sum - a[i]) + 1) * m, 使其大于sum。
S= (b[1]*x[1]+b[2]*x[2]+…+b[n]*x[n])%m
=w * (a[1]*x[1]+a[2]*x[2]+…+a[n]*x[n]) % m
∑a[i] * x[i] = (S * v) % m,令ans = (S * v) % m
由于a[i]是超递增序列,当ans > a[i]时,x[i]一定为1。因为a[1] +.... + a[i - 1] < a[i] 。由此便可解出x[i].
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 35;
ll t, n, b[maxn], v, m, s, a[maxn], x[maxn];
int main(){
scanf("%lld", &t);
while(t--){
memset(x, 0, sizeof(x));
scanf("%lld", &n);
for(int i=0; i<n; i++) scanf("%lld", &b[i]);
scanf("%lld%lld%lld", &v, &m, &s);
ll sum = 0;
for(int i=0; i<n; i++){
a[i] = (b[i] * v) % m;
if(a[i] <= sum)
a[i] += m * ((sum - a[i]) / m + 1);
}
ll ans = (v * s) % m;
for(int i=n-1; i>=0; i--){
if(a[i] <= ans){
x[i] = 1;
ans -= a[i];
}
}
for(int i=0; i<n; i++) printf("%lld", x[i]);
puts("");
}
return 0;
}