Codeforces 895C Square Subsets(状压DP 或 异或线性基)

题目链接  Square Subsets

这是白书原题啊

先考虑状压DP的做法

2到70总共19个质数,所以考虑状态压缩。

因为数据范围是70,那么我们统计出2到70的每个数的个数然后从2考虑到70。

设dp[x][mask]为考虑到x这个数的时候,x这个数和之前的所有数中,选出某些数,他们的乘积分解质因数,所有的指数对2取模之后,

状态为mask的方案数。

然后就可以转移了……这个状压DP花了我好几个小时……真是弱啊

哦对最后还要特判1的情况。

每个1选或不选都可以,然后考虑只选1的情况,累加即可。

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)

typedef long long LL;

const LL mod = 1e9 + 7;
const int N  = 3e6 + 10;
const int M  = 1e5 + 10;

int c[101], p[30], m[101];
int n, x, cnt, now, all;
bool flag;
LL two[M], f[2][N];

void up(LL &a, LL b) { a = (a + b) % mod;}

void init(){
	two[0] = 1;
	rep(i, 1, 100000) two[i] = two[i - 1] * 2 % mod;

	scanf("%d", &n);
	rep(i, 1, n) scanf("%d", &x), ++c[x];

	rep(i, 2, 70){
		flag = true;
		rep(j, 2, i - 1) if (i % j == 0){
			flag = false;
			break;
		}
		if (flag) p[cnt++] = i;
	}

	rep(i, 1, 70){
		int y = i;
		rep(j, 0, cnt - 1){
			int tt = 0;
			while (y % p[j] == 0) y /= p[j], ++tt;
			if (tt & 1) m[i] |= (1 << j);
		}
	}
}

int main(){

	init();
	all = (1 << cnt) - 1;
	rep(i, 2, 70){
		if (c[i] == 0) continue;
		memset(f[now ^ 1], 0, sizeof f[now ^ 1]);
		LL a1 = two[c[i] - 1], a2 = (a1 - 1 + mod) % mod;

		up(f[now ^ 1][m[i]], a1);
		up(f[now ^ 1][0], a2);

		rep(mask, 0, all) up(f[now ^ 1][mask ^ m[i]], f[now][mask] * a1 % mod);
		rep(mask, 0, all) up(f[now ^ 1][mask], f[now][mask] * a2 % mod);
		rep(mask, 0, all) up(f[now ^ 1][mask], f[now][mask]);
		now ^= 1;
	}

	LL ans = f[now][0];
	ans = (ans * two[c[1]]) % mod;
	up(ans, (two[c[1]] - 1 + mod) % mod);
	printf("%lld\n", ans);
	return 0;
}

还有一种就是考虑异或线性基的做法。

如果一个数可以被当前线性基中的数表示出来,那么这个数就相当于一个完全平方数。

选与不选两种状态。

令最后线性基中的数的个数为$x$

最后答案就是$2^{n - x} - 1$

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)

typedef long long LL;

const LL mod = 1e9 + 7;
const int N  = 3e6 + 10;
const int M  = 1e5 + 10;

int c[101], p[30], m[101];
int n, x, cnt, now, all;
bool flag;
LL two[M], f[2][N];

void up(LL &a, LL b) { a = (a + b) % mod;}

void init(){
	two[0] = 1;
	rep(i, 1, 100000) two[i] = two[i - 1] * 2 % mod;

	scanf("%d", &n);
	rep(i, 1, n) scanf("%d", &x), ++c[x];

	rep(i, 2, 70){
		flag = true;
		rep(j, 2, i - 1) if (i % j == 0){
			flag = false;
			break;
		}
		if (flag) p[cnt++] = i;
	}

	rep(i, 1, 70){
		int y = i;
		rep(j, 0, cnt - 1){
			int tt = 0;
			while (y % p[j] == 0) y /= p[j], ++tt;
			if (tt & 1) m[i] |= (1 << j);
		}
	}
}

int main(){

	init();
	all = (1 << cnt) - 1;
	rep(i, 2, 70){
		if (c[i] == 0) continue;
		memset(f[now ^ 1], 0, sizeof f[now ^ 1]);
		LL a1 = two[c[i] - 1], a2 = (a1 - 1 + mod) % mod;

		up(f[now ^ 1][m[i]], a1);
		up(f[now ^ 1][0], a2);

		rep(mask, 0, all) up(f[now ^ 1][mask ^ m[i]], f[now][mask] * a1 % mod);
		rep(mask, 0, all) up(f[now ^ 1][mask], f[now][mask] * a2 % mod);
		rep(mask, 0, all) up(f[now ^ 1][mask], f[now][mask]);
		now ^= 1;
	}

	LL ans = f[now][0];
	ans = (ans * two[c[1]]) % mod;
	up(ans, (two[c[1]] - 1 + mod) % mod);
	printf("%lld\n", ans);
	return 0;
}
时间: 2024-10-08 18:38:04

Codeforces 895C Square Subsets(状压DP 或 异或线性基)的相关文章

CodeForces 21D Traveling Graph 状压dp+欧拉回路

题目链接:点击打开链接 题意: 给定n个点m条边的无向图 求从1点开始经过每条边至少一次最后回到1点的最小路程 显然就是找一条路径可重复的欧拉回路 思路: 首先对于欧拉回路的结论是:所有点的度数都为偶数 因为所有边至少经过一次,那么可以把题意转换成加最少多少条边使得图满足以上结论 而加的边目的是为了把奇度数转成偶度数,先floyd一下得到任意点间加边的最小花费 dp[i]表示状态i下度数都为偶数的最小花费. 状压dp,把i状态下,所有未选择的点中挑2个奇度数的转移即可. #include <cs

Codeforces 580D-Kefa and Dishes(状压DP)

原题链接:http://codeforces.com/problemset/problem/580/D 题意:在n个数字中有顺序地选择m个数字,每个数字对应有个值ai,每取一个数字答案加上ai,并且存在k个关系:x y c,如果x恰好排在y的前面,那么答案再加上ci的值.输出最大值. 思路:状压dp.dp[i][j]中,i是已经选了若干个数的情况,j是最后一个被选取的数,i从选取1个到m个枚举下去,j从第1个数到第n个数进行枚举就能得到答案. AC代码: 1 #include<iostream>

COdeforces#417D Cunning Gena(状压DP)

A boy named Gena really wants to get to the "Russian Code Cup" finals, or at least get a t-shirt. But the offered problems are too complex, so he made an arrangement with his n friends that they will solve the problems for him. The participants

Codeforces 544E Remembering Strings 状压dp

题目链接 题意: 给定n个长度均为m的字符串 下面n行给出字符串 下面n*m的矩阵表示把对应的字母修改成其他字母的花费. 问: 对于一个字符串,若它是easy to remembering 当 它存在一个字母,使得这个字母在这一列是独一无二的. 要使得n个字符串都是easy to remembering 的最小花费. 第一个样例是把第一列的4个a中3个a修改成别的字母,所以花费为3. 思路: 显然是个状压dp,但需要一点转化. 首先得到一个结论: 对于某一列,设这一列的字母是 a,a,b,b,a

Codeforces 895C - Square Subsets

题意: 给了n个数,要求有几个子集使子集中元素的和为一个数的平方. 题解: 因为每个数都可以分解为质数的乘积,所有的数都小于70,所以在小于70的数中一共只有19个质数.可以使用状压DP,每一位上0表示这个质数的个数为偶数个,1表示为奇数个.这样的话,如果某个数为一个数的平方的话,那么每个质数个数都是偶数,用0可以表示.从1-70开始状压DP,先存下每个数出现多少次,然后dp转移,dp转移时分别计算某个数出现奇数次还是偶数次的方案数. 这里有一个公式:C(n,0)+C(n,2)+--=C(n,1

CodeForces 327E Axis Walking(状压DP+卡常技巧)

Iahub wants to meet his girlfriend Iahubina. They both live in Ox axis (the horizontal axis). Iahub lives at point 0 and Iahubina at point d. Iahub has n positive integers a1, a2, ..., an. The sum of those numbers is d. Suppose p1, p2, ..., pn is a p

Codeforces 327E Axis Walking (状压dp lowbit优化)

E. Axis Walking time limit per test:3 seconds memory limit per test:512 megabytes Iahub wants to meet his girlfriend Iahubina. They both live in Ox axis (the horizontal axis). Iahub lives at point 0 and Iahubina at point d. Iahub has n positive integ

CodeForces 907E Party(bfs+状压DP)

Arseny likes to organize parties and invite people to it. However, not only friends come to his parties, but friends of his friends, friends of friends of his friends and so on. That's why some of Arseny's guests can be unknown to him. He decided to

Codeforces 327E Axis Walking 状压dp(水

题目链接:点击打开链接 题意: 给定n个数,随意排列. 给定k个违禁数b[]. 问:有多少个排列使得这个排列的 n项前缀和中不出现违禁数. (formally,if it's a legal permutation, sum[i] != b[j] (1<=i<=n, 1<=j<=k)) sum[0] = 0; sum[i] = sum[i-1]+a[permutaion[i]]; ==java党表示被tle,心疼自己T^T #include <stdio.h> cons