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();
}
原文地址:https://www.cnblogs.com/hznumqf/p/13720558.html