省选模拟赛Day5T1 致命漏洞

92 篇文章 0 订阅
68 篇文章 0 订阅

致命漏洞

 

【问题描述】

 

魔法学家Rutherford.D.Imbalance最近发表了一个生成伪随机序列的方法:

B_0=1

B_(n+1)=2B_n                ,2B_n<2^T

B_(n+1)=(2B_n xor X) mod 2^T  ,2B_n>=2^T

其中XT给定。

他使用这个方法来构造他研制的式神的神经指令加密系统,可是作为Synophia大陆稀有的以研究数学为工作的你的好友Lwins_Alpha,敏锐地发现了这个方法存在着致命的漏洞。其中最严重的问题莫过于可以通过一些数学手段快速计算得这个序列的任意一项。Lwins_Alpha本想写信告知Imbalance这个问题,但是她突然发现Imbalance不可能看懂她的复杂的数学符号,于是她向你——以扫地为生来隐藏自己真实身份的大陆有史以来最伟大的式神制造师——Lwins_***求助了。(***为待定内容,视你最后提交上来的文件包而定)

为了证明Imbalance生成伪随机序列的方法确实有致命漏洞,最简单的方法莫过于编写一个能快速计算B_n的式神,然后将它邮寄给Imbalance。而在式神的其他部分都由你的徒弟Lwins_Lights编写的情况下,你只需要使用Pascal/C/C++语言编写最核心的部分——输入n,T,X,输出B_n:因为Lwins_Alpha为了考验你的IQ,居然没有告诉你快速计算B_n的具体方法!

不能让她小瞧你,抱着这样的信念,你开始了自己的工作。

 

【输入格式】

 

输入文件bug.in共三行,每行一个正整数,分别为n,T,X

 

【输出格式】

 

输出文件bug.out仅包含一个正整数B_n

 

【输入样例】

 

7

7

7

 

【输出样例】

 

    7

 

【数据约定

 

对于20%的数据:n<=10^5, T<=20

对于55%的数据:n<=10^5

对于100%的数据:n<=10^30, T<=100, 0<X<2^T


我还写过一篇关于这类题的博客:点击打开链接

一种套路:用矩阵乘法描述线性递推。

这道题看似递推式有两个,就想不到可以用矩阵了。

其实这两个递推式可以合二为一 Bn= ( 2*B(n-1) xor (X & (B >> (T-1) == 1)  ) ) % ( 2 ^ T )

而其中每一个运算都是线性变换(楼主又在误导小朋友)

所以用一个复杂点(大一点)的矩阵也是可以维护的。

矩阵优化的矩阵大概有两种,第一种是答案矩阵(不是表示递推关系的那个矩阵)唯一的一列存Fi到F(i+n)

而第二种是唯一的一列只存Fi的各个维度

因为B>>(T-1)==1这个东西必须作为矩阵的一个元素。(不然还是没法计算,除非你能找到一个非常神奇的运算符来重载)

所以我们要把B看做矩阵的一列,每一格存B在二进制下某一位的值,其中最下面那一格表示的就是B>>(T-1)

然后2*B(n-1)其实就是B<<1,联系后面的X & (B >> (T-1) == 1)

我们可以把乘法重载为 & 

加法重载为 ^

再用我在前文推荐的博客上的检验方法检验其是否满足结合律:

A & ( B1 ^ B2 ^ B3 ^ B4......  ) == ( (A & B1) ^ (A & B2) ^ (A & B3)......  )

不难发现上述等式恒成立,所以这种重载方法满足结合律,可以用矩阵快速幂来加速。

这样,对这种套路的思路也应该更加清晰了。

ACcode:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define maxn 105
using namespace std;

int n[maxn],T,x[maxn];
char S[maxn];

void read(int *A)
{
	scanf("%s",S);
	int len=strlen(S);
	for(int i=0;i<len>>1;i++) swap(S[i],S[len-i-1]);
	for(;len;)
	{
		int tmp=0;
		for(int i=len-1;i>=0;i--)
		{
			tmp=tmp*10+S[i]-'0';
			S[i]=tmp/2+'0';
			tmp&=1;
		}
		for(;len && S[len-1]=='0';len--);
		A[++A[0]]=tmp;
	}
}

void print(int *A)
{
	int sta[maxn],tp=0;
	for(int len=T;len;)
	{
		int tmp=0;
		for(int i=len-1;i>=0;i--)
		{
			tmp=tmp*2+A[i];
			A[i]=tmp/10;
			tmp=tmp%10;
		}
		for(;len && A[len-1]==0;len--);
		sta[tp++]=tmp;
	}
	for(;tp;) printf("%d",sta[--tp]);
}

struct Mat
{
	int a[maxn][maxn];
	int n;
	
	void clear(int N)
	{
		n=N;
		memset(a,0,sizeof a);
	}
	
	void Unit(int N)
	{
		clear(N);
		for(int i=0;i<N;i++) a[i][i]=1;
	}
	
	Mat operator *(const Mat &nxt)const
	{
		Mat ret;ret.clear(n);
		for(int i=0;i<n;i++)
			for(int k=0;k<n;k++) if(a[i][k])
				for(int j=0;j<n;j++) if(nxt.a[k][j])
					ret.a[i][j]^=1;
		return ret;
	}
	
}A,B;

int tmp[maxn];
int main()
{
	
	freopen("bug.in","r",stdin);
	freopen("bug.out","w",stdout);
	
	read(n);
	scanf("%d",&T);
	read(x);
	
	A.clear(T);
	for(int i=1;i<T;i++) A.a[i][i-1]=1;
	for(int i=0;i<T;i++) A.a[i][T-1]=x[i+1];
	
	B.Unit(T);
	for(int i=1;i<=n[0];i++)
	{
		if(n[i]) B=A*B;
		A=A*A;
	}
	
	for(int i=0;i<T;i++) tmp[i]=B.a[i][0];
	print(tmp);
}

注:太弱了,高精度转进制都不会。。。。。其实就是高精度除低精。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值