Codeforces 895.C Square Subsets
4 seconds
256 megabytes
standard input
standard output
Petya was late for the lesson too. The teacher gave him an additional task. For some array a Petya should find the number of different ways to select non-empty subset of elements from it in such a way that their product is equal to a square of some integer.
Two ways are considered different if sets of indexes of elements chosen by these ways are different.
Since the answer can be very large, you should find the answer modulo 109 + 7.
First line contains one integer n (1 ≤ n ≤ 105) — the number of elements in the array.
Second line contains n integers ai (1 ≤ ai ≤ 70) — the elements of the array.
Print one integer — the number of different ways to choose some elements so that their product is a square of a certain integer modulo 109 + 7.
4
1 1 1 1
15
4
2 2 2 2
7
5
1 2 4 5 8
7
In first sample product of elements chosen by any way is 1 and 1 = 12. So the answer is 2^4 - 1 = 15.
In second sample there are six different ways to choose elements so that their product is 4, and only one way so that their product is 16. So the answer is 6 + 1 = 7.
大致题意:选若干个数使得乘积为完全平方数,问方案数.
分析:对于完全平方数,它的每个质因子的次数都是偶数.这道题中ai ≤ 70,质因子最多只有19个,可以考虑状压,记一个状态sta,第i位表示第i个质因子的次数为偶数还是奇数,状态的变化可以通过位运算中的异或运算来解决.那么可以设计状态f[i][j]表示前i个数字中状态为j的方案数,每个数字有选或不选两种选择,转移也比较明确.比较遗憾的是n比较大,2^19 * n不足以通过此题.需要换一种状态的表达方式.
首先状态j,也就是第二维是不能去掉的,关键是第一维该换成什么.2^19已经比较大了,那么第一维也不能很大,能想到的就是表示成1到大小为i的数字中,状态为j的方案数:f[i][j],这样的话不一位一位地去考虑,而是去考虑每个数字出现的次数.转移的时候枚举i和上一次的状态j,那么j能够转移到状态j ^ sta[i]和j,转移到j ^ sta[i]就需要取奇数个i,方案数就是C(n,1) + C(n,3) + ...... = 2^(n-1).j转移到j取偶数个就行了,方案数还是2^(n-1).
最后的答案就是f[70][0].
一道非常好的题.设计状态保留必须的,替换其它的,从题目的要求和信息中找到可以替换的状态.也可以从另一个思路来理解这个替换:不需要知道i在哪几位被选,只需要知道i被选了多少次,记录出现的次数并利用组合数学的知识就可以解决了.
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm> using namespace std; typedef long long ll; const int mod = 1e9 + ; ll n, a[], cnt[], jie[];
int prime[] = { , , , , , , , , , , , , , , , , , , };
int f[][( << ) + ], stu[]; int main()
{
cin >> n;
for (int i = ; i <= n; i++)
{
cin >> a[i];
cnt[a[i]]++;
}
jie[] = ;
for (int i = ; i <= n; i++)
jie[i] = (jie[i - ] * ) % mod;
for (int i = ; i <= ; i++)
{
for (int j = ; j < ; j++)
{
int x = i;
while (x % prime[j] == )
{
stu[i] ^= ( << j);
x /= prime[j];
}
}
}
f[][] = ;
for (int i = ; i <= ; i++)
{
for (int j = ; j < ( << ); j++)
{
if (!cnt[i])
f[i][j] = f[i - ][j];
else
{
f[i][j] = (f[i][j] + jie[cnt[i] - ] * f[i - ][j] % mod) % mod;
f[i][j ^ stu[i]] = (f[i][j ^ stu[i]] + jie[cnt[i] - ] * f[i - ][j] % mod) % mod;
}
}
}
f[][]--;
if (f[][] < )
f[][] += mod;
cout << f[][] << endl; return ;
}
Codeforces 895.C Square Subsets的更多相关文章
- Codeforces Round #448 C. Square Subsets
题目链接 Codeforces Round #448 C. Square Subsets 题解 质因数 *质因数 = 平方数,问题转化成求异或方程组解的个数 求出答案就是\(2^{自由元-1}\) , ...
- Codeforces 895C - Square Subsets
895C - Square Subsets 思路:状压dp. 每个数最大到70,1到70有19个质数,给这19个质数标号,与状态中的每一位对应. 状压:一个数含有这个质因子奇数个,那么他状态的这一位是 ...
- Codeforces 895C Square Subsets(状压DP 或 异或线性基)
题目链接 Square Subsets 这是白书原题啊 先考虑状压DP的做法 $2$到$70$总共$19$个质数,所以考虑状态压缩. 因为数据范围是$70$,那么我们统计出$2$到$70$的每个数的 ...
- Codeforces 828B Black Square(简单题)
Codeforces 828B Black Square(简单题) Description Polycarp has a checkered sheet of paper of size n × m. ...
- CF895C: Square Subsets && 【BZOJ2844】albus就是要第一个出场
CF895C: Square Subsets && [BZOJ2844]albus就是要第一个出场 这两道题很类似,都是线性基的计数问题,解题的核心思想也一样. CF895C Squa ...
- Codeforces 895C Square Subsets:状压dp【组合数结论】
题目链接:http://codeforces.com/problemset/problem/895/C 题意: 给你n个数a[i].(n <= 10^5, 1 <= a[i] <= ...
- Codeforces 895C - Square Subsets 状压DP
题意: 给了n个数,要求有几个子集使子集中元素的和为一个数的平方. 题解: 因为每个数都可以分解为质数的乘积,所有的数都小于70,所以在小于70的数中一共只有19个质数.可以使用状压DP,每一位上0表 ...
- Codeforces Round #448 (Div. 2)C. Square Subsets
可以用状压dp,也可以用线型基,但是状压dp没看台懂... 线型基的重要性质 性质一:最高位1的位置互不相同 性质二:任意一个可以用这些向量组合出的向量x,组合方式唯一 性质三:线性基的任意一个子集异 ...
- CodeForces 1A Theatre Square
A - Theatre Square Time Limit:2000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u ...
随机推荐
- mui搜索框 搜索点击事件
<div class="mui-input-row mui-search"> <input type="search" class=" ...
- JavaScript学习笔记(三)——对象
第四章 理解对象 1 说明 对象的状态:属性,行为:方法: 对象定义放在花括号内: 用冒号分隔属性名和属性值: 用逗号分隔属性名和属性值对,包括方法: 最后一个属性值后面不加逗号: 属性名可以是任何字 ...
- Catch That Cow:BFS:加标记数组:不加标记数组
Catch That Cow Problem Description Farmer John has been informed of the location of a fugitive cow a ...
- day-19 多种优化模型下的简单神经网络tensorflow示例
如下样例基于tensorflow实现了一个简单的3层深度学习入门框架程序,程序主要有如下特性: 1. 基于著名的MNIST手写数字集样例数据:http://yann.lecun.com/exdb/m ...
- 创建image
摘要: 本节演示如何通过 Web GUI 和 CLI 两种方法创建 Image. 本节演示如何通过 Web GUI 和 CLI 两种方法创建 Image. OpenStack 为终端用户提供了 Web ...
- 产品需求文档(PRD)的写作 【转】
产品需求文档(PRD)的写作 一.文章的摘要介绍 无论我们做什么事都讲究方式方法,写产品需求文档(以下称PRD文档)也是如此,之前我通过四篇文章分享了自己写PRD文档的一些方法,而这一篇文章主要是 ...
- Java 通过先序中序序列生成二叉树
题目 二叉树的前序以及后续序列,以空格间隔每个元素,重构二叉树,最后输出二叉树的三种遍历方式的序列以验证. 输入: 1 2 3 4 5 6 7 8 9 10 3 2 5 4 1 7 8 6 10 9 ...
- 元素相加交换另解&puts的一个用法
#include<iostream> using namespace std; int main(){ int a,b; cin>>a>>b; a^=b; b^=a ...
- map的默认排序和自定义排序
STL的容器map为我们处理有序key-value形式数据提供了非常大的便利,由于内部红黑树结构的存储,查找的时间复杂度为O(log2N). 一般而言,使用map的时候直接采取map<typen ...
- lintcode-450-K组翻转链表
450-K组翻转链表 给你一个链表以及一个k,将这个链表从头指针开始每k个翻转一下. 链表元素个数不是k的倍数,最后剩余的不用翻转. 样例 给出链表 1->2->3->4->5 ...