zoukankan      html  css  js  c++  java
  • GYM-100199H Cracking' RSA 数论,高斯消元求自由变元

    GYM-100199H Cracking' RSA 数论,高斯消元求自由变元

    题意

    首先题目友好的给定一个(t) ,再给定一个数(m) 表示接下来有(m) 个数,表示接下来的数字都由素数表中的前(t)个数组成。

    从这(m)个数中选择一些子集,要求子集的元素乘积是完全平方数的子集的个数。

    [1 leq t leq 100\ 1leq m leq 100\ 1leq b_i leq 10^9 ]

    分析

    一个数是完全平方数,等价于其各个质因子的幂次方是偶数。

    这个(m)的数据很容易让人想到高斯消元。

    对于任意子集,相当于要求满足(t) 个方程。

    这个方程表示幂的和是偶数,也就是模2下等于0。

    那么这题就容易了

    最后对这个方程组高斯消元求出自由变元的个数,即表示一种方案。这些方案进行任意叠加都是可行的。最后去掉空集即可。

    此题难点可能在于高斯消元,这里偷懒直接使用网上的模板。

    int a[maxn][maxn];  // 增广矩阵
    int x[maxn];        // 解集
    bool free_x[maxn];  // 标记是否为不确定的变元
    
    // 高斯消元法解方程组
    //-2表示有浮点数解,但无整数解
    //-1表示无解
    //0表示唯一解
    //大于0表示无穷解,并返回自由变元的个数
    //有equ个方程,var个变元
    //增广矩阵行数为equ,分别为0到equ-1,列数为var+1,分别为0到var.
    int Gauss(int equ, int var, int mod)
    {
    	int i, j, k;
    	int max_r;//当前这列绝对值最大的行.
    	int col;//当前处理的列
    	int ta, tb;
    	int LCM;
    	int temp;
    	int free_x_num;
    	int free_index;
    	for (int i = 0; i <= var; i++)
    	{
    		x[i] = 0;
    		free_x[i] = true;
    	}
    	//转换为阶梯阵.
    	col = 0;//当前处理的列
    	for (k = 0; k < equ && col < var; k++, col++)
    	{
    		// 枚举当前处理的行.
    		// 找到该col列元素绝对值最大的那行与第k行交换(为了在除法时减小误差)
    		max_r = k;
    		for (i = k + 1; i < equ; i++)
    		{
    			if (abs(a[i][col]) > abs(a[max_r][col])) max_r = i;
    		}
    		if (max_r != k)
    		{// 与第k行交换.
    			for (j = k; j < var + 1; j++) swap(a[k][j], a[max_r][j]);
    		}
    		if (a[k][col] == 0)
    		{// 说明该col列第k行以下全是0了,则处理当前行的下一列.
    			k--;
    			continue;
    		}
    		for (i = k + 1; i < equ; i++)
    		{// 枚举要删去的行.
    			if (a[i][col] != 0)
    			{
    				LCM = Lcm(abs(a[i][col]), abs(a[k][col]));
    				ta = LCM / abs(a[i][col]);
    				tb = LCM / abs(a[k][col]);
    				if (a[i][col] * a[k][col] < 0)tb = -tb;//异号的情况是相加
    				for (j = col; j < var + 1; j++)
    				{
    					a[i][j] = ((a[i][j] * ta - a[k][j] * tb) % mod + mod) % mod;
    				}
    			}
    		}
    	}
    	//无解的情况
    	for (i = k; i < equ; i++)
    	{
    		if (a[i][col] != 0) return -1;
    	}
    	// 无穷解的情况
    	if (k < var)
    	{
    		//自由变元有var-k个,即不确定的变元至少有var-k个.
    		for (i = k - 1; i >= 0; i--)
    		{
    			free_x_num = 0; // 用于判断该行中的不确定的变元的个数,如果超过1个,则无法求解,它们仍然为不确定的变元.
    			for (j = 0; j < var; j++)
    			{
    				if (a[i][j] != 0 && free_x[j]) free_x_num++, free_index = j;
    			}
    			if (free_x_num > 1) continue; // 无法求解出确定的变元.
    			// 说明就只有一个不确定的变元free_index,那么可以求解出该变元,且该变元是确定的.
    			temp = a[i][var];
    			for (j = 0; j < var; j++)
    			{
    				if (a[i][j] != 0 && j != free_index) temp -= a[i][j] * x[j] % mod;
    				temp = (temp % mod + mod) % mod;
    			}
    			x[free_index] = (temp / a[i][free_index]) % mod;//求出该变元.
    			free_x[free_index] = 0;//该变元是确定的.
    		}
    		return var - k; //自由变元有var-k个.
    	}
    	//唯一解的情况 
    	for (i = var - 1; i >= 0; i--)
    	{
    		temp = a[i][var];
    		for (j = i + 1; j < var; j++)
    		{
    			if (a[i][j] != 0) temp -= a[i][j] * x[j];
    			temp = (temp % mod + mod) % mod;
    		}
    		while (temp % a[i][i] != 0) temp += mod;
    		x[i] = (temp / a[i][i]) % mod;
    	}
    	return 0;
    }
    
    
    int prime[maxn];   //保存质数
    int vis[maxn];     //是否被筛
    int euler_sieve(int n) {
        int cnt = 0;
        for (int i = 2;i <=  n; i++) {
            if (!vis[i]) prime[cnt++] = i;
            for (int j = 0; j < cnt; j++) {
                if (i * prime[j] > n) break;
                vis[i * prime[j]] = 1;
                if (i % prime[j] == 0) break;
            }
        }
        return cnt;  //返回x小于等于n的s素数的个数
    }
    
    #define MAXN 9999
    #define MAXSIZE 10
    #define DLEN 4
    
    class BigNum
    {
    private:
    	int a[2005];    //可以控制大数的位数 
    	int len;       //大数长度
    public:
    	BigNum() { len = 1; memset(a, 0, sizeof(a)); }   //构造函数
    	BigNum(const int);       //将一个int类型的变量转化为大数
    	BigNum(const char*);     //将一个字符串类型的变量转化为大数
    	BigNum(const BigNum&);  //拷贝构造函数
    	BigNum& operator=(const BigNum&);   //重载赋值运算符,大数之间进行赋值运算
    
    	friend istream& operator>>(istream&, BigNum&);   //重载输入运算符
    	friend ostream& operator<<(ostream&, BigNum&);   //重载输出运算符
    
    	BigNum operator+(const BigNum&) const;   //重载加法运算符,两个大数之间的相加运算 
    	BigNum operator-(const BigNum&) const;   //重载减法运算符,两个大数之间的相减运算 
    	BigNum operator*(const BigNum&) const;   //重载乘法运算符,两个大数之间的相乘运算 
    	BigNum operator/(const int&) const;    //重载除法运算符,大数对一个整数进行相除运算
    
    	BigNum operator^(const int&) const;    //大数的n次方运算
    	int    operator%(const int&) const;    //大数对一个int类型的变量进行取模运算    
    	bool   operator>(const BigNum& T)const;   //大数和另一个大数的大小比较
    	bool   operator>(const int& t)const;      //大数和一个int类型的变量的大小比较
    
    	void print();       //输出大数
    };
    BigNum::BigNum(const int b)     //将一个int类型的变量转化为大数
    {
    	int c, d = b;
    	len = 0;
    	memset(a, 0, sizeof(a));
    	while (d > MAXN)
    	{
    		c = d - (d / (MAXN + 1)) * (MAXN + 1);
    		d = d / (MAXN + 1);
    		a[len++] = c;
    	}
    	a[len++] = d;
    }
    BigNum::BigNum(const char* s)     //将一个字符串类型的变量转化为大数
    {
    	int t, k, index, l, i;
    	memset(a, 0, sizeof(a));
    	l = strlen(s);
    	len = l / DLEN;
    	if (l % DLEN)
    		len++;
    	index = 0;
    	for (i = l - 1; i >= 0; i -= DLEN)
    	{
    		t = 0;
    		k = i - DLEN + 1;
    		if (k < 0)
    			k = 0;
    		for (int j = k; j <= i; j++)
    			t = t * 10 + s[j] - '0';
    		a[index++] = t;
    	}
    }
    BigNum::BigNum(const BigNum& T) : len(T.len)  //拷贝构造函数
    {
    	int i;
    	memset(a, 0, sizeof(a));
    	for (i = 0; i < len; i++)
    		a[i] = T.a[i];
    }
    BigNum& BigNum::operator=(const BigNum& n)   //重载赋值运算符,大数之间进行赋值运算
    {
    	int i;
    	len = n.len;
    	memset(a, 0, sizeof(a));
    	for (i = 0; i < len; i++)
    		a[i] = n.a[i];
    	return *this;
    }
    istream& operator>>(istream& in, BigNum& b)   //重载输入运算符
    {
    	char ch[MAXSIZE * 4];
    	int i = -1;
    	in >> ch;
    	int l = strlen(ch);
    	int count = 0, sum = 0;
    	for (i = l - 1; i >= 0;)
    	{
    		sum = 0;
    		int t = 1;
    		for (int j = 0; j < 4 && i >= 0; j++, i--, t *= 10)
    		{
    			sum += (ch[i] - '0') * t;
    		}
    		b.a[count] = sum;
    		count++;
    	}
    	b.len = count++;
    	return in;
    
    }
    ostream& operator<<(ostream& out, BigNum& b)   //重载输出运算符
    {
    	int i;
    	cout << b.a[b.len - 1];
    	for (i = b.len - 2; i >= 0; i--)
    	{
    		cout.width(DLEN);
    		cout.fill('0');
    		cout << b.a[i];
    	}
    	return out;
    }
    
    BigNum BigNum::operator+(const BigNum& T) const   //两个大数之间的相加运算
    {
    	BigNum t(*this);
    	int i, big;      //位数   
    	big = T.len > len ? T.len : len;
    	for (i = 0; i < big; i++)
    	{
    		t.a[i] += T.a[i];
    		if (t.a[i] > MAXN)
    		{
    			t.a[i + 1]++;
    			t.a[i] -= MAXN + 1;
    		}
    	}
    	if (t.a[big] != 0)
    		t.len = big + 1;
    	else
    		t.len = big;
    	return t;
    }
    BigNum BigNum::operator-(const BigNum& T) const   //两个大数之间的相减运算 
    {
    	int i, j, big;
    	bool flag;
    	BigNum t1, t2;
    	if (*this > T)
    	{
    		t1 = *this;
    		t2 = T;
    		flag = 0;
    	}
    	else
    	{
    		t1 = T;
    		t2 = *this;
    		flag = 1;
    	}
    	big = t1.len;
    	for (i = 0; i < big; i++)
    	{
    		if (t1.a[i] < t2.a[i])
    		{
    			j = i + 1;
    			while (t1.a[j] == 0)
    				j++;
    			t1.a[j--]--;
    			while (j > i)
    				t1.a[j--] += MAXN;
    			t1.a[i] += MAXN + 1 - t2.a[i];
    		}
    		else
    			t1.a[i] -= t2.a[i];
    	}
    	t1.len = big;
    	while (t1.a[len - 1] == 0 && t1.len > 1)
    	{
    		t1.len--;
    		big--;
    	}
    	if (flag)
    		t1.a[big - 1] = 0 - t1.a[big - 1];
    	return t1;
    }
    
    BigNum BigNum::operator*(const BigNum& T) const   //两个大数之间的相乘运算 
    {
    	BigNum ret;
    	int i, j, up;
    	int temp, temp1;
    	for (i = 0; i < len; i++)
    	{
    		up = 0;
    		for (j = 0; j < T.len; j++)
    		{
    			temp = a[i] * T.a[j] + ret.a[i + j] + up;
    			if (temp > MAXN)
    			{
    				temp1 = temp - temp / (MAXN + 1) * (MAXN + 1);
    				up = temp / (MAXN + 1);
    				ret.a[i + j] = temp1;
    			}
    			else
    			{
    				up = 0;
    				ret.a[i + j] = temp;
    			}
    		}
    		if (up != 0)
    			ret.a[i + j] = up;
    	}
    	ret.len = i + j;
    	while (ret.a[ret.len - 1] == 0 && ret.len > 1)
    		ret.len--;
    	return ret;
    }
    BigNum BigNum::operator/(const int& b) const   //大数对一个整数进行相除运算
    {
    	BigNum ret;
    	int i, down = 0;
    	for (i = len - 1; i >= 0; i--)
    	{
    		ret.a[i] = (a[i] + down * (MAXN + 1)) / b;
    		down = a[i] + down * (MAXN + 1) - ret.a[i] * b;
    	}
    	ret.len = len;
    	while (ret.a[ret.len - 1] == 0 && ret.len > 1)
    		ret.len--;
    	return ret;
    }
    int BigNum::operator %(const int& b) const    //大数对一个int类型的变量进行取模运算    
    {
    	int i, d = 0;
    	for (i = len - 1; i >= 0; i--)
    	{
    		d = ((d * (MAXN + 1)) % b + a[i]) % b;
    	}
    	return d;
    }
    BigNum BigNum::operator^(const int& n) const    //大数的n次方运算
    {
    	BigNum t, ret(1);
    	int i;
    	if (n < 0)
    		exit(-1);
    	if (n == 0)
    		return 1;
    	if (n == 1)
    		return *this;
    	int m = n;
    	while (m > 1)
    	{
    		t = *this;
    		for (i = 1; i << 1 <= m; i <<= 1)
    		{
    			t = t * t;
    		}
    		m -= i;
    		ret = ret * t;
    		if (m == 1)
    			ret = ret * (*this);
    	}
    	return ret;
    }
    bool BigNum::operator>(const BigNum& T) const   //大数和另一个大数的大小比较
    {
    	int ln;
    	if (len > T.len)
    		return true;
    	else if (len == T.len)
    	{
    		ln = len - 1;
    		while (a[ln] == T.a[ln] && ln >= 0)
    			ln--;
    		if (ln >= 0 && a[ln] > T.a[ln])
    			return true;
    		else
    			return false;
    	}
    	else
    		return false;
    }
    bool BigNum::operator >(const int& t) const    //大数和一个int类型的变量的大小比较
    {
    	BigNum b(t);
    	return *this > b;
    }
    
    void BigNum::print()    //输出大数
    {
    	int i;
    	cout << a[len - 1];
    	for (i = len - 2; i >= 0; i--)
    	{
    		cout.width(DLEN);
    		cout.fill('0');
    		cout << a[i];
    	}
    	cout << endl;
    }
    
    
    
    int main() {
    	freopen("rsa.in", "r", stdin);
    	freopen("rsa.out", "w", stdout);
        int cnt = euler_sieve(800);
        int t = readint(), m = readint();
        for (int j = 0; j < m; j++) {
            int tmp = readint();
            for (int i = 0; i < cnt; i++) {
                if (tmp % prime[i] == 0) {
                    while (tmp % prime[i] == 0) tmp /= prime[i], a[i][j]++;
                }
                a[i][j] %= 2;
            }
        }
        int num = Gauss(t, m, 2);
    	BigNum base(2);
    	base = base ^ num;
    	base = base - 1;
    	base.print();
    }
    
  • 相关阅读:
    maven
    Antd@4.x Form的常用方法
    数组排序 js
    链式调用
    react 非登陆页面得公共部分配置方法
    react异常处理
    Field 输入框的bug
    promise的用法
    异步return公共数据方法,
    小程序打开pdf文件
  • 原文地址:https://www.cnblogs.com/hznumqf/p/13720558.html
Copyright © 2011-2022 走看看