/*
题意:给你n个数,问你从这n个选择一些数相乘后为完全平方数的个数?
tag: 高斯消元 列方程
分析:对于每一个数我们有选和不选两种选择,我们将其看成变量xi。然后问题就转化成从n个数中选择一些使得这些数唯一分解式中
指数对2取模的异或和为0。 然后列出方程求出自由元的个数num答案就是2^num - 1
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int mod = 1000000007;
int n;
int pm[2000], npm;
void get_prime() {
bool is_heshu[2000 + 100];
memset(is_heshu, 0, sizeof(is_heshu));
for(int i=2; i<=2000; i++) {
if(is_heshu[i]) continue;
pm[npm++] = i;
for(int j=i+i; j<=2000; j+=i) is_heshu[j] = true;
}
}
int equ, var; //有equ个方程 var个变量
int A[1000][1000];
int free_num;
int gauss() {
free_num = 0;
int k, col;
for(k=0, col=0; k<equ&&col<var; k++,col++) {
int max_r = k;
for(int i=k+1; i<equ; i++)
if(abs(A[i][col]) > abs(A[max_r][col])) max_r = i;
if(max_r != k)
for(int j=k; j<=var; j++) swap(A[k][j], A[max_r][j]);
if(A[k][col] == 0) {
k--;
free_num++;
continue;
}
for(int i=k+1; i<equ; i++) {
if(A[i][col] == 0) continue;
for(int j=col; j<=var; j++)
A[i][j] ^= A[k][j];
}
}
for(int i=k; i<equ; i++)
if(A[i][col] != 0) return -1; //无解
return free_num;
}
int qk_mod(int a, int b) {
long long res = 1;
while(b) {
if(b&1)
res = ((long long)res*(long long)a)%mod;
a = ((long long)a*(long long)a)%mod;
b >>= 1;
}
return res;
}
int main() {
int T; scanf("%d", &T);
get_prime();
// printf("npm = %d\n", npm);
// for(int i=0; i<npm; i++) printf("%d ", pm[i]);
// printf("\n");
int kase = 0;
while(T--) {
scanf("%d", &n);
memset(A, 0, sizeof(A));
for(int j=0; j<n; j++) {
LL t; scanf("%lld", &t);
for(int i=0; i<npm; i++) {
LL cnt = 0, tp = t;
while(tp%pm[i]==0 && tp) tp/=pm[i], cnt++;
A[i][j] = cnt%2;
}
}
equ = npm; var = n;
int res = gauss();
printf("Case #%d:\n", ++kase);
// printf("res = %d\n", res);
if(res == -1) printf("0\n");
else printf("%d\n", qk_mod(2, res)-1);
}
return 0;
}
<pre name="code" class="cpp">/*
题意:给你一个N*M的方格, 方格内本来有一些数字, 但是不幸的是数字消失了, 现在只记住了一些数字中的最小值得地方,
问你有多少种填数字的方案满足这些谷点?
tag:dfs + 容斥 + 状态压缩dp
分析:我们考虑从小往大往格子里面填数字, 定义dp[i][j]为已经填了i个格子, X填涂的情况为j的方案数。
那么dp[i][j] = dp[i-1][j] * (往.填的方案数) + dp[i-1][k] k为j中的一个X没有填涂。
这种计数方法会使一些.的位置变成X, 比如...X, 我们可能会将X..X也计入, 因此下面就需要去重, 去重的时候我们使用
容斥原理即可。 参考论文: 状态压缩动态规划中的状态与时间
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
typedef long long LL;
const int mod = 772002;
int N, M;
char Map[10][10];
LL dp[30][1<<12];
int num1[1<<15];
int cnt(int st) {
int res = 0;
for(int i=0; i<15; i++) if(((st>>i)&1) == 1) res++;
return res;
}
int dx[] = {-1, -1, -1, 0, 0, 1, 1, 1};
int dy[] = {-1, 0, 1, -1, 1, -1, 0, 1};
bool inside(int x, int y) {
return (x>=0&&x<N&&y>=0&&y<M);
}
int judge(int x, int y, map<pair<int, int>, int>&mp, int st) { //看看当前.周围有没有没有涂过色的X
for(int i=0; i<8; i++) {
int nx = x + dx[i], ny = y + dy[i];
if(!inside(nx, ny)) continue;
if(Map[nx][ny] == 'X' && ((st>>mp[make_pair(nx, ny)])&1)==0)
return true;
}
return false;
}
int tp2[1<<14];
LL calc() {
memset(dp, 0, sizeof(dp));
int idx = 0;
map<pair<int, int>, int> mp;
for(int i=0; i<N; i++) for(int j=0; j<M; j++)
if(Map[i][j] == 'X') mp[make_pair(i, j)] = idx++;
for(int j=0; j<(1<<idx); j++) { //预处理X填涂的情况为j的时候可以往.填的方案数
int tp = 0;
for(int r=0; r<N; r++) for(int c=0; c<M; c++) {
if(Map[r][c] == 'X' && ((j>>mp[make_pair(r, c)])&1)==0) tp++;
else if(judge(r, c, mp, j)) tp++;
}
int tp3 = N*M - tp;
tp2[j] = tp3;
}
dp[0][0] = 1;
for(int i=1; i<=N*M; i++) for(int j=0; j<(1<<idx); j++) {
if(num1[j] > i) continue;
for(int k=0; k<idx; k++) if(((j>>k)&1)==1) dp[i][j] += dp[i-1][j^(1<<k)];
dp[i][j] %= mod;
int tp = tp2[j] - i + 1;
if(tp > 0) dp[i][j] += tp*dp[i-1][j];
dp[i][j] %= mod;
}
return dp[N*M][(1<<idx)-1];
}
bool pan(int x, int y) {
for(int i=0; i<8; i++) {
int nx = x + dx[i], ny = y + dy[i];
if(!inside(nx, ny)) continue;
if(Map[nx][ny] == 'X') return false;
}
return true;
}
LL ans;
void dfs(int x, int y, int flog) {
if(x==N) {
if(!flog) ans += calc();
else ans -= calc();
ans = (ans%mod + mod)%mod;
return ;
}
int tpx = x, tpy = y + 1;
if(tpy == M) tpy = 0, tpx +=1;
dfs(tpx, tpy, flog);
if(Map[x][y]=='.' && pan(x, y)) {
Map[x][y] = 'X';
dfs(x, y, flog^=1);
Map[x][y] = '.';
}
}
int main() {
int kase = 0;
for(int i=0; i<(1<<14); i++) {
num1[i] = cnt(i);
}
while(scanf("%d%d", &N, &M) == 2) {
for(int i=0; i<N; i++) scanf("%s", Map[i]);
ans = 0;
bool flog = false;
for(int i=0; i<N; i++) for(int j=0; j<M; j++) if(Map[i][j] == 'X'){
for(int k=0; k<8; k++) {
int nx = i + dx[k], ny = j + dy[k];
if(!inside(nx, ny)) continue;
if(Map[nx][ny] == 'X') flog = true;
}
}
if(flog) {
printf("Case #%d: 0\n", ++kase);
continue;
}
dfs(0, 0, 0);
printf("Case #%d: %lld\n",++kase, ans);
}
return 0;
}