题目描述
鏼尔德是六兄弟中的老三,他喜欢老四弗斯,他希望弗斯高兴。
鏼尔德给弗斯发了一封信,为了防止别人知道,他将其加密了,请你帮助弗斯解密。
鏼尔德选择了n个正整数a1,a2…an,保证ai>a1+a2+…ai-1。令q=2^64,保证q>a1+a2+…+an。他用一个和q互质正整数r构造出bi=(ai*r) mod q。他想给弗斯发的消息是一个长度为n的二进制串c,ci=0或1。他将b1,b2,…,bn,(b1c1+b2c2+…bncn) mod q告诉了弗斯,请你帮弗斯解密出c。
子任务一:40分,保证n≤20。
子任务二:60分,保证n<64。
解题思路
很容易想到大约40以下的都可以折半搜索。问题是更大的怎么弄。
似乎除了解出a[]和r别无选择。
考虑一个n>40,可以发现
a[1]<=264−n
a
[
1
]
<=
2
64
−
n
,尝试枚举a[1]取值,然后再考虑r的取值,假如情况不多,肯定能试到正确的取值,这之后求r的逆元,就能把a[]还原。注意到a的性质,我们可以从后往前考虑c[]的取值,贪心地拼出(b1c1+b2c2+…bncn) mod q.
那么考虑具体怎么做。
b[1]=a[1]∗r−i∗264
b
[
1
]
=
a
[
1
]
∗
r
−
i
∗
2
64
取极大的k
b[1]2k=a[1]2k∗r−i∗264−k
b
[
1
]
2
k
=
a
[
1
]
2
k
∗
r
−
i
∗
2
64
−
k
,也就是模
264−k
2
64
−
k
意义下。
那么在此意义下对
a[1]2k
a
[
1
]
2
k
取逆元,然后乘到左边,就可以得出r在此意义下的值。
但我们要的是2^64意义下的,可以暴力枚举更高位,然后求逆元,然后拿去check。由于数据合法,肯定能枚举出正确的东西。
后面的复杂度比较迷,不过令44为阈值会很好
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
const int N=1e5+5;
int n,i,j,st[10000000+5],mid,pd[10000000+5];
ull v,b[N],tmp,ha[10000000+5],mo=1e7,sum,x,lim,a[N],rev,rb,tv,pw,pp,kan,ra,r;
char s1[N];
int hash(ull x)
{
int k=x%mo;
while (pd[k]&&ha[k]!=x)
k=(k+1)%mo;
return k;
}
void dfs(ull x,ull y,ull z,int lim,int s)
{
if (x==lim)
{
int t;
if (s==1)
{
t=hash(z);
ha[t]=z;
st[t]=y;
pd[t]=1;
}
else
{
t=hash(v-z);
if (pd[t])
{
y|=st[t];
t=hash(v-z);
ull i,tmp;
fo(i,1,n)
if (y&((ull)1<<i-(ull)1)) s1[i]='1',tmp+=b[i]*(i<=mid);else s1[i]='0';
printf("%s\n",s1+1);
exit(0);
}
}
return ;
}
dfs(x+s,y,z,lim,s);
dfs(x+s,y|((ull)1<<(ull)x-1),z+b[x],lim,s);
}
int check(ull rev)
{
sum=a[1];
fo(j,2,n)
{
ull tmp=b[j]*rev;
a[j]=tmp;
if (a[j]<=sum||sum+a[j]<a[j]||sum+a[j]<sum)
{
return 0;
}
sum+=a[j];
}
tv=v*rev;
fd(j,n,1)
{
if (tv>=a[j]) tv-=a[j],s1[j]='1';else
s1[j]='0';
}
return (tv==0);
}
ull ksm(ull x,ull y)
{
ull ret=1;
while (y)
{
if (y&1) ret=ret*x;
y>>=1;
x=x*x;
}
return ret;
}
ull Rev(ull x,ull &mo,ull &rb)
{
pw=64;
while (x%2==0)
{
pw--;
x/=2;
}
mo=(ull)1<<pw;
if (pw==64) mo=0;
rb=ksm(x,((ull)1<<(pw-1))-(ull)1);
}
int main()
{
freopen("t3.in","r",stdin);
// freopen("t3.out","w",stdout);
scanf("%d",&n);
fo(i,1,n)
scanf("%llu\n",b+i);
scanf("%llu",&v);
if (n<=44)
{
mid=n/2;
dfs(1,0,0,mid+1,1);
dfs(n,0,0,mid,-1);
}else
{
lim=1<<(64-n);
fo(i,1,lim)
{
a[1]=i;
Rev(a[1],mo,ra);
r=ra*(b[1]>>(ull)(64-pw));
if (mo) r%=mo;
do
{
rev=ksm(r,((ull)1<<(ull)63)-(ull)1);
if (check(rev))
{
printf("%s\n",s1+1);
}
if (!mo) break;
r+=mo;
}while (r>mo);
}
}
}