这几天学习了一下Burnside,而Polya只是Burnside的一个特例。
Burnside可以用下面这个公式概括:
本质不同的方案数 = 所有置换的不动点数之和 / 置换的个数
什么是不动点?
如果对一个染色方案执行一次置换F后该方案保持不变,则该置换是F的一个不动点。
常见的置换方法有旋转、翻转。
考虑一个圆环:
可以绕n条对称轴翻转(不论n是奇还是偶) , 可以绕中心旋转0,1,2,3...n-1位 ,故共有2*n种置换。
怎样求对于"旋转i位" 这个置换 的不动点数 ?
每个循环的长度 l = lcm(i,n) / i = n / gcd(i , n)
循环的个数 g = gcd(i , n)
其等效模型为 “长为g的一个圆环不考虑旋转时的染色方案数” ,此时不加限制的方案数为c^g 。
当n很大时显然不能枚举i ,这时需要按g值对i进行分类 , 设f(g)为 gcd(i , n) = g的不同i的个数 ,即gcd(i/g, n/g) = 1的不同i的个数 ,显然 f(g) = phi(n/g) . [欧拉函数]
因此可以通过枚举n的因子g来确定答案。
于是这类“圆环旋转问题”的等价类计数问题可以用这样一个模板解决:
①枚举n的所有因子g
② sum += fixpoint(g) * phi(n /g)
③ ans = sum / 置换的个数.
其中fixpoint(g) 就是 “长为g的一个圆环不考虑旋转时的染色方案数” 这个问题的答案。
而通常问题的难点也是fixpoint(g)的计算,题目一般会给出一些限制。
可能是dp 、矩阵快速幂 、 数学推导的综合体 。 。。
还是通过几个例题来加深理解吧。。
http://vjudge.net/contest/view.action?cid=47722#overview
最后两个神题,实在感到力不从心,希望大神们指点一二。。
直接套Burnside即可。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
LL pow3[25] , ans[25] ;
int gcd(int a,int b) {
return b==0 ? a : gcd(b , a%b) ;
}
LL f(int n) {
LL sum = 0 ;
for(int i=0; i<n; i++) sum += pow3[gcd(i ,n)];
if(n&1) sum += (LL)n * pow3[(n+1)/2] ;
else sum += (LL)n/2*pow3[n/2+1] + (LL)n/2*pow3[n/2] ;
return sum / (n*2) ;
}
int main()
{
pow3[0] = 1 ;
for(int i=1; i<25; i++) pow3[i] = pow3[i-1] * 3;
for(int i=1; i<25; i++) ans[i] = f(i) ;
int n ;
while(scanf("%d" , &n) , ~n) {
printf("%I64d\n" , ans[n]) ;
}
return 0;
}
入门基础。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
LL pow3[25] , ans[25] ;
int gcd(int a,int b) {
return b==0 ? a : gcd(b , a%b) ;
}
LL f(int n) {
LL sum = 0 ;
for(int i=0; i<n; i++) sum += pow3[gcd(i ,n)];
if(n&1) sum += (LL)n * pow3[(n+1)/2] ;
else sum += (LL)n/2*pow3[n/2+1] + (LL)n/2*pow3[n/2] ;
return sum / (n*2) ;
}
int main()
{
pow3[0] = 1 ;
for(int i=1; i<25; i++) pow3[i] = pow3[i-1] * 3;
for(int i=1; i<25; i++) ans[i] = f(i) ;
int n ;
while(scanf("%d" , &n) , ~n) {
printf("%I64d\n" , ans[n]) ;
}
return 0;
}
置换有:
旋转0°、90°、180°、270° 。
按n的奇偶考虑对称轴。
import java.io.*;
import java.util.*;
import java.math.*;
public class Main {
public static void main(String args[]) {
Scanner cin = new Scanner(System.in) ;
while(cin.hasNext()) {
int n = cin.nextInt() ;
BigInteger c = cin.nextBigInteger() ;
BigInteger sum = BigInteger.ZERO ;
if(n%2==1) {
// 不动
sum = sum.add(c.pow(n * n));
// 转90°
sum = sum.add(c.pow((n*n-1)/4 + 1));
// 转180°
sum = sum.add(c.pow((n*n-1)/2 + 1)) ;
// 转 270°
sum = sum.add(c.pow((n*n-1)/4 + 1)) ;
// 翻
sum = sum.add( c.pow((n*n-n)/2+n).multiply(BigInteger.valueOf(4)) ) ;
}
else {
sum = sum.add(c.pow(n*n)) ;
sum = sum.add(c.pow(n*n/4)).add(c.pow(n*n/2)).add(c.pow(n*n/4)) ;
sum = sum.add((c.pow((n*n - n)/2 + n).add(c.pow(n*n/2))).multiply(BigInteger.valueOf(2))) ;
}
BigInteger ans = sum.divide(BigInteger.valueOf(8)) ;
System.out.println(ans.toString()) ;
}
cin.close();
}
}
n很大,用欧拉函数优化。 注意n和p不一定互质,不存在n的逆 ,怎么解决呢,前面少乘一次n就行了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
typedef long long LL ;
const LL maxn = 32000 ;
int pri[maxn/4] , tot ;
bool flag[maxn+1] ;
void init_prime(){
for(int i=2; i*i<=maxn; i++) if(!flag[i]) {
for(LL j=i*i; j<=maxn; j+=i) flag[j] = true;
}
for(int i=2; i<=maxn; i++) if(!flag[i]) pri[tot++] = i ;
}
LL pow_mod(LL a,LL n ,LL mod) {
LL ret = 1;
while(n) {
if(n&1) ret = ret*a%mod ;
a = a*a%mod ;
n>>=1 ;
}
return ret ;
}
LL phi(LL n){
LL ret = n ;
for(int i=0 ; pri[i]*pri[i]<=n ; i++) if(n % pri[i]==0) {
ret = ret / pri[i] * (pri[i]-1) ;
while(n%pri[i] == 0) n /= pri[i] ;
}
if(n>1) ret = ret / n * (n-1) ;
return ret ;
}
int N , P ;
LL ans ;
int fac[100],cnt[100],cn ;
void dfs(int g , int d) {
if(d == cn) {
ans += pow_mod(N , g-1 , P) * phi(N / g) ;
ans %= P ;
return ;
}
int tmp = 1 ;
for(int i=0; i<=cnt[d] ;i++) {
dfs(g*tmp , d+1) ;
tmp *= fac[d] ;
}
}
int F(LL n , LL p){
cn = 0 ;
for(int i=0; pri[i]*pri[i]<=n ;i++) if(n%pri[i] == 0) {
fac[cn] = pri[i] ;
cnt[cn] = 0 ;
while(n % pri[i] == 0) {
cnt[cn]++ ;
n /= pri[i] ;
}
cn++ ;
}
if(n>1) fac[cn] = n , cnt[cn++] = 1 ;
ans = 0 ;
dfs(1, 0) ;
return ans ;
}
int main()
{
init_prime() ;
int T;
scanf("%d" , &T);
while(T--){
scanf("%d%d" , &N , &P) ;
printf("%d\n" , F(N , P)) ;
}
return 0;
}
同同样n很大。 用矩阵处理限制关系。
// 一篇不错的博客: http://hi.baidu.com/billdu/item/62319f2554c7cac9a5275a0d
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
typedef long long LL ;
const LL MOD = 9973 ;
const LL maxn = 31650 ;
LL M ;
LL pri[maxn/4] , pri_tot ;
bool _flag[maxn+1] ;
void init_prime(){
for(LL i=2; i*i<=maxn; i++) if(!_flag[i]) {
for(LL j=i*i; j<=maxn; j+=i) _flag[j] = true ;
}
for(LL i=2; i<=maxn; i++) if(!_flag[i]) pri[pri_tot++] = i;
}
vector<LL>fac;
struct Function{ // 用于计算n的因子,保存在 vector<LL> fac
LL a[100], p[100], tot ;
void FenJie(LL n) {
tot = 0 ;
for(LL i=0; pri[i]*pri[i]<=n; i++) if(n % pri[i] == 0) {
p[tot] = 0 ;
a[tot] = pri[i] ;
while(n % pri[i] ==0) n/=pri[i] , p[tot]++ ;
tot++ ;
}
if(n > 1) a[tot] = n , p[tot++] = 1 ;
}
void dfs(LL z , LL d) {
if(d == tot) {
fac.push_back(z) ;
return ;
}
LL ap = 1 ;
for(LL i=0; i<=p[d]; i++) {
dfs(z * ap , d+1) ;
ap *= a[d] ;
}
}
void get_fac(LL n) {
fac.clear() ;
FenJie(n) ;
dfs(1 , 0) ;
}
}sol;
struct Mat{
LL a[10][10] ;
Mat(LL x=0){
memset(a , 0 ,sizeof(a)) ;
for(LL i=0; i<M; i++) a[i][i] = x;
}
Mat operator * (const Mat &b)const {
Mat ret ;
for(LL i=0; i<M ;i++){
for(LL j=0; j<M; j++){
LL tmp = 0 ;
for(LL k=0; k<M; k++){
tmp += a[i][k] * b.a[k][j] ;
}
ret.a[i][j] = tmp % MOD ;
}
}
return ret ;
}
Mat operator ^ (LL n) {
Mat ret(1) ;
while(n) {
if(n&1) ret = ret**this ;
*this = *this**this ;
n>>=1 ;
}
return ret;
}
};
LL Pow(LL a , LL n) {
LL ret = 1 ;
a %= MOD ;
while(n) {
if(n&1) ret = ret * a % MOD ;
a = a * a % MOD ;
n>>=1 ;
}
return ret ;
}
LL inv(LL x) {
return Pow(x , MOD-2) ;
}
LL phi(LL n) {
LL ret = n;
for(LL i=0; pri[i]*pri[i]<=n; i++) if(n % pri[i] == 0) {
ret = ret / pri[i] * (pri[i] - 1) ;
while(n % pri[i] == 0) n /= pri[i] ;
}
if(n > 1) ret = ret / n * (n-1) ;
return ret ;
}
Mat ok ;
LL fixpoLL(LL n) { // 不考虑旋转,长度为n时 的方案数。
LL ret = 0 ;
Mat A = ok ;
A = A ^ n ;
for(LL i=0; i<M; i++)
ret += A.a[i][i] ;
ret %= MOD ;
return ret ;
}
LL solve(LL n) {
LL ret = 0 ;
sol.get_fac(n) ;
for(size_t i=0; i<fac.size() ;i++) {
ret += fixpoLL(fac[i]) * phi(n / fac[i]) ;
ret %= MOD ;
}
ret = ret * inv(n) % MOD ;
return ret ;
}
int main()
{
init_prime() ;
LL n , k , T ;
scanf("%I64d" , &T) ;
while(T--) {
scanf("%I64d%I64d%I64d" , &n ,&M , &k) ;
for(LL i=0; i<M; i++)
for(LL j=0; j<M; j++)
ok.a[i][j] = 1 ;
for(LL i=0; i<k; i++) {
int u ,v ;
scanf("%d%d" , &u , &v) ;
u-- , v-- ;
ok.a[u][v] = ok.a[v][u] = 0 ;
}
LL ans = solve(n) ;
printf("%I64d\n" , ans) ;
}
return 0;
}
与前面的题大同小异,可以按“是否与第一位相同”构造矩阵计算。
推荐播客: http://hi.baidu.com/billdu/item/62319f2554c7cac9a5275a0d
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
typedef long long LL ;
const LL MOD = 1e9 + 7 ;
const int maxn = 31650 ;
struct Mat{
LL a[2][2] ;
Mat(int x=0){memset(a,0,sizeof(a)) ; a[0][0]=a[1][1]=x; }
Mat operator * (const Mat &b)const {
Mat ret;
for(int i=0; i<2; i++)
for(int j=0;j<2;j++){
for(int k=0;k<2;k++)
ret.a[i][j] += a[i][k] * b.a[k][j] ;
ret.a[i][j] %= MOD ;
}
return ret ;
}
Mat operator ^ (LL n) {
Mat ret(1) ;
while(n) {
if(n&1) ret = ret * *this ;
*this=*this * *this ;
n>>=1 ;
}
return ret ;
}
};
int pri[maxn/4] , pri_tot ;
bool _flag[maxn+1] ;
void init_prime() {
for(int i=2; i*i <=maxn; i++) if(!_flag[i])
for(int j=i*i ; j<=maxn; j+=i) _flag[j] = true ;
for(int i=2; i<=maxn; i++) if(!_flag[i]) pri[pri_tot++] = i ;
}
void gcd(LL a , LL b, LL &d , LL &x ,LL &y) {
if(!b) d = a , x = 1 , y = 0 ;
else gcd(b , a%b ,d , y ,x), y -= a/b * x ;
}
LL inv(LL a) {
LL d , x , y ;
gcd(a , MOD , d , x ,y) ;
return d==1 ? (x+MOD)%MOD : -1 ;
}
LL Pow(LL a , LL n) {
LL ret = 1 ;
while(n) {
if(n&1) ret = ret * a % MOD ;
a = a * a % MOD ;
n>>=1 ;
}
return ret ;
}
LL phi(LL n) {
LL ret = n ;
for(int i=0; pri[i]*pri[i]<=n ;i++) if(n%pri[i] == 0) {
ret = ret / pri[i] * (pri[i] - 1) ;
while(n%pri[i] == 0) n/= pri[i] ;
}
if(n>1) ret = ret / n * (n-1) ;
return ret ;
}
vector<LL>fac ;
struct Function{
LL a[100] , p[100] , tot ;
void FenJie(LL n) {
tot = 0 ;
for(int i=0; pri[i]*pri[i] <= n; i++) if(n % pri[i] == 0) {
a[tot] = pri[i] , p[tot] = 0 ;
while( n % pri[i] == 0) p[tot]++ , n/=pri[i] ;
tot++ ;
}
if(n>1) a[tot] = n , p[tot++] = 1 ;
}
void dfs(LL z , int d) {
if(d == tot) {
fac.push_back(z) ;
return ;
}
LL ap = 1 ;
for(int i=0; i<=p[d] ; i++) {
dfs(z*ap , d+1) ;
ap *= a[d] ;
}
}
void get_fac(LL n) {
fac.clear() ;
FenJie(n) ;
dfs(1 , 0) ;
}
}Fun;
LL cal(LL k , LL n) {
if(n==1) return 0;
Mat A ;
A.a[0][0]=k-2 , A.a[0][1]=k-1 ;
A.a[1][0]=1 , A.a[1][1]=0 ;
A = A ^ (n-1);
return A.a[0][1] * k % MOD ;
}
LL solve(LL n , LL k) {
LL sum = 0 ;
Fun.get_fac(n) ;// cout<<fac.size()<<endl ;
for(size_t i=0; i<fac.size() ;i++) {
sum += cal(k-1 , fac[i]) * phi(n/fac[i]) ;
sum %= MOD ;
}
sum = sum * k % MOD ;
sum = sum * inv(n) % MOD ;
return sum ;
}
int main()
{
ios::sync_with_stdio(0) ;
init_prime() ;
LL n , k;
while(cin>>n>>k) cout<<solve(n , k)<<endl ;
return 0;
}
这题有难度,其中递推推导部分不容易。
而且n不存在逆元,解决方法是 (a/b)%c = (a%(b*c)) / b
推荐播客: http://hi.baidu.com/spellbreaker/item/d8bb3bda5af30be6795daa93
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
typedef long long LL ;
const int maxn = 31650 ;
LL MOD ;
LL mul(LL a , LL b){
a%=MOD;
if(b<0) a = -a , b=-b ;
if(a<0) a += MOD;
LL ret = 0 ;
while(b){
if(b&1LL && (ret+=a)>=MOD) ret -= MOD ;
if((a<<=1) >= MOD) a -= MOD ;
b >>= 1 ;
}
return ret ;
}
struct Mat{
LL a[2][2] ;
Mat (int x=0) {a[0][0]=a[1][1]=x ; a[0][1]=a[1][0]=0; }
Mat operator * (const Mat & b)const {
Mat ret ;
for(int i=0;i<2;i++)
for(int j=0; j<2; j++) {
for(int k=0; k<2; k++)
if((ret.a[i][j] += mul(a[i][k] , b.a[k][j])) >= MOD )
ret.a[i][j] -= MOD ;
}
return ret ;
}
Mat operator ^ (LL n) {
Mat ret(1) ;
while(n) {
if(n&1) ret = ret * *this ;
*this=*this* *this ;
n>>=1 ;
}
return ret ;
}
};
LL F(LL n) {
if(n == 0) return 1 ;
Mat A ;
A.a[0][0] = 3 , A.a[0][1] = -1 ;
A.a[1][0] = 1 , A.a[1][1] = 0 ;
A = A^(n-1) ;
LL ret = mul(A.a[1][0] , 3) + A.a[1][1] ;
while(ret >= MOD) ret -= MOD ;
return ret ;
}
LL G(LL n) {
if(n<=1) return 0 ;
LL ret = F(n) - F(n-1) - 1 ;
ret = mul(ret , 2) ;
return ret ;
}
LL pri[maxn/4] , pri_tot ;
bool _flag[maxn+1] ;
void init_prime(){
for(LL i=2; i*i<=maxn; i++) if(!_flag[i]) {
for(LL j=i*i; j<=maxn; j+=i) _flag[j] = true ;
}
for(LL i=2; i<=maxn; i++) if(!_flag[i]) pri[pri_tot++] = i;
}
vector<LL>fac;
struct Function{ // 用于计算n的因子,保存在 vector<LL> fac
LL a[100], p[100], tot ;
void FenJie(LL n) {
tot = 0 ;
for(LL i=0; pri[i]*pri[i]<=n; i++) if(n % pri[i] == 0) {
p[tot] = 0 ;
a[tot] = pri[i] ;
while(n % pri[i] ==0) n/=pri[i] , p[tot]++ ;
tot++ ;
}
if(n > 1) a[tot] = n , p[tot++] = 1 ;
}
void dfs(LL z , LL d) {
if(d == tot) {
fac.push_back(z) ;
return ;
}
LL ap = 1 ;
for(LL i=0; i<=p[d]; i++) {
dfs(z * ap , d+1) ;
ap *= a[d] ;
}
}
void get_fac(LL n) {
fac.clear() ;
FenJie(n) ;
dfs(1 , 0) ;
}
}sol;
LL phi(LL n) {
LL ret = n;
for(LL i=0; pri[i]*pri[i]<=n; i++) if(n % pri[i] == 0) {
ret = ret / pri[i] * (pri[i] - 1) ;
while(n % pri[i] == 0) n /= pri[i] ;
}
if(n > 1) ret = ret / n * (n-1) ;
return ret ;
}
LL solve(LL n , LL m) {
MOD = n * m ;
LL sum = 0 ;
sol.get_fac(n) ;
for(size_t i = 0; i<fac.size() ;i++) {
sum += mul(F(fac[i])+G(fac[i]) , phi(n/fac[i])) ;
if( sum >= MOD) sum -= MOD ;
else if(sum < 0) sum += MOD ;
}
sum /= n ;
return sum ;
}
int main()
{
init_prime() ;
LL n , m;
ios::sync_with_stdio(0) ;
while(cin>>n>>m) {
cout<<solve(n , m)<<endl ;
}
return 0;
}
做完前面的题,这题应该没任何难度。
import java.util.* ;
import java.math.* ;
import java.io.* ;
public class Main {
static long MOD = (long)1e9+7 ;
static public void main(String args[]) {
Scanner cin = new Scanner(System.in);
int T = cin.nextInt();
for(int cas = 1; cas <= T; cas++) {
long c = cin.nextLong() , n = cin.nextLong();
long sum = 0 ;
for(long i=0; i<n; i++) {
sum += pow(c , gcd(i , n)) ;
sum %= MOD ;
}
if(n%2 == 1) {
sum += n * pow(c , (n+1)/2) ;
sum %= MOD ;
}
else {
sum += n/2*pow(c , n/2+1) ;
sum += n/2*pow(c , n/2) ;
sum %= MOD ;
}
sum = sum * inv(2*n) % MOD ;
int ans = (int)sum ;
System.out.printf("Case #%d: " , cas) ;
System.out.println(ans) ;
}
cin.close();
}
static long gcd(long a,long b){
return b==0 ? a : gcd(b , a%b) ;
}
static long pow(long a , long n){
long ret = 1 ;
while(n != 0) {
if(n%2 == 1) ret = ret * a % MOD;
a = a * a % MOD ;
n>>=1 ;
}
return ret ;
}
static long inv(long x) {
return pow(x , MOD-2) ;
}
}
白书上的例题:共24种置换。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int cnt[10] ;
int A[15] ;
int f(int n , int m) {
int ret = A[n/m] ;
for(int i=1; i<=6; i++) {
if(cnt[i] % m) return 0 ;
ret /= A[cnt[i] / m] ;
}
return ret ;
}
int main()
{
A[0]=1 ;
for(int i=1; i<15; i++) A[i] = A[i-1] * i ;
int T ;
scanf("%d" , &T) ;
while(T--) {
memset(cnt , 0, sizeof(cnt)) ;
for(int i=1 , x; i<=12; i++) {
scanf("%d" ,&x);
cnt[x]++ ;
}
int sum = 0 ;
sum += f(12 , 1);
sum += 3 * f(12 ,4) + 3 * f(12 , 2) + 3 * f(12 , 4) ;
for(int i=1; i<=6; i++){
for(int j=1;j<=6;j++){
cnt[i]-- , cnt[j]-- ;
if(cnt[i]>=0 && cnt[j]>=0) sum += 6 * f(10 , 2) ;
cnt[i]++ , cnt[j]++ ;
}
}
sum += 4 * f(12 , 3) + 4 * f(12 , 3) ;
int ans = sum / 24 ;
printf("%d\n" , ans) ;
}
return 0;
}
同样只是练手题。
import java.util.*;
import java.io.*;
import java.math.*;
public class Main {
static BigInteger A[] = new BigInteger[50] ;
static int n , a[] = new int[5];
static void init() {
A[0] = BigInteger.ONE ;
for(int i=1; i<=40; i++)
A[i] = A[i-1].multiply(BigInteger.valueOf(i)) ;
}
static int gcd(int x, int y) {
return y==0 ? x : gcd(y , x%y) ;
}
static BigInteger rotate(int m) {
BigInteger ret = A[n / m] ;
for(int i=1; i<=3 ;i++) {
if(a[i] % m != 0) return BigInteger.ZERO ;
ret = ret.divide(A[a[i]/m]) ;
}
return ret;
}
static BigInteger flip(int m) {
BigInteger ret = A[m / 2] ;
for(int i=1; i<=3; i++) {
if(a[i] % 2 != 0 || a[i]<0 ) return BigInteger.ZERO ;
ret = ret.divide(A[ a[i]/2 ]) ;
}
return ret ;
}
//===========================================================================
static public void main(String args[]){
Scanner cin = new Scanner(System.in);
init() ;
int T = cin.nextInt();
while(T-- != 0) {
n = 0 ;
for(int i=1; i<=3; i++){
a[i] = cin.nextInt();
n += a[i] ;
}
BigInteger sum = BigInteger.ZERO ;
// deal rotate
for(int i=0; i<n; i++) {
sum = sum.add( rotate( n / gcd(i , n) ) );
}
// deal flip
if(n%2 == 1) {
for(int i=1; i<=3; i++) {
a[i] -- ;
if(a[i] >= 0 ) sum = sum.add( BigInteger.valueOf(n).multiply( flip(n-1) ) ) ;
a[i] ++ ;
}
}
else {
sum = sum.add( BigInteger.valueOf(n/2).multiply( flip(n) ) ) ;
for(int i=1; i<=3; i++){
for(int j=1; j<=3; j++) {
a[i]-- ; a[j]-- ;
if(a[i]>=0 && a[j]>=0)
sum = sum.add( BigInteger.valueOf(n/2).multiply( flip(n-2) ) ) ;
a[i]++ ; a[j]++ ;
}
}
}
// output
BigInteger ans = sum.divide(BigInteger.valueOf(n * 2)) ;
System.out.println(ans.toString()) ;
}
cin.close();
}
}
和I题基本相同。
import java.util.*;
import java.io.* ;
import java.math.*;
public class Main {
static int maxn = 50 * 50 * 6 ;
static int c[] = new int[7] ;
static long A[] = new long[maxn+1] , invA[] = new long[maxn+1] ;
static long MOD = (long)1e9 + 7 ;
static public void main(String args[]) {
Scanner cin = new Scanner(System.in) ;
init() ;
int T = cin.nextInt() ;
while(T-->0) {
int n = cin.nextInt() ;
int N = 0 ;
for(int i=1; i<=6; i++) {
c[i] = cin.nextInt();
N += c[i] ;
}
long sum = 0 ;
// 不动
sum += fixpoint(N , 1) ; //System.out.println(N);
// 绕对称的棱的中点的连线转180°
sum += 6 * fixpoint(N , 2) ;
// 绕对顶角转 120° 、 240°
sum += 4 * 2 * fixpoint(N , 3) ;
// 绕相对的面的中心的连线转90° 、180°、270°
if(n%2 == 1) {
for(int i=1; i<=6; i++)
for(int j=1; j<=6; j++) {
c[i]-- ; c[j]-- ;
if(c[i]>=0 && c[j]>=0) sum += 3 * ( 2*fixpoint(N-2 , 4) + fixpoint(N-2 , 2) ) ;
c[i]++; c[j]++ ;
}
}
else {
sum += 3 * ( 2*fixpoint(N , 4) + fixpoint(N , 2) ) ;
}
sum %= MOD ;
long ans = sum * inv(24) % MOD ;
System.out.println(ans) ;
}
}
static void init() {
A[0] = 1 ;
for(int i=1; i<=maxn; i++) A[i] = A[i-1] * i % MOD ;
for(int i=0; i<=maxn; i++) invA[i] = inv(A[i]) ;
}
static long pow(long a , long n) {
long ret = 1 ;
while(n>0) {
if(n%2 == 1) ret = ret * a % MOD ;
a = a * a % MOD ;
n>>=1 ;
}
return ret ;
}
static long inv(long a) {
return pow(a , MOD-2) ;
}
static long fixpoint(int n , int m) {
long ret = A[n/m] ;
for(int i=1; i<=6; i++){
if(c[i] % m != 0 || c[i]<0) return 0 ;
ret = ret * invA[c[i] / m] % MOD ;
}
return ret ;
}
}
L CSU 1436 Revenge of the Round Table
连续相同的个数不能超过k , dp.....
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL ;
const int maxn = 1010 ;
const LL MOD = 1000003 ;
int dp[maxn][maxn][2] ,N, K ; // dp[i][j][k] : 长为i,后面j个相同,k: 最后一位是否与第一位相同。
LL Pow(LL a , LL n) {
LL ret =1 ;
while(n) {
if(n&1) ret = ret * a % MOD ;
a = a * a % MOD ;
n>>=1 ;
}
return ret ;
}
LL inv(LL a) {
return Pow(a , MOD-2) ;
}
int phi(int n){
int ret = n ;
for(int i=2; i*i<=n ; i++) if(n%i==0) {
ret = ret /i * (i-1) ;
while(n%i == 0) n/=i ;
}
if(n>1) ret= ret / n *(n-1) ;
return ret ;
}
LL fixpoint(LL n) {
if(K>=N) return Pow(2 , n) ;
memset(dp , 0 ,sizeof(dp)) ;
dp[1][1][1] = 2 ;
for(int i=1; i<n; i++) {
for(int j=1; j<=K; j++){
for(int k=0; k<2; k++){
dp[i+1][j+1][k] += dp[i][j][k] ;
dp[i+1][j+1][k] %= MOD ;
dp[i+1][1][k^1] += dp[i][j][k] ;
dp[i+1][1][k^1] %= MOD ;
}
}
}
LL ans = 0 ;
for(int s=1; s<=K && s<n; s++){ // 前面已经有s个相同的了。
for(int i=1; i<=K && i+s<=N; i++) ans += dp[n-s][i][1] ;
for(int i=1; i+s<=K; i++) ans += dp[n-s][i][0] ;
ans %= MOD ;
}
return ans ;
}
int main()
{
cin>>N>>K;
LL sum = 0 ;
for(int i=1; i<=N; i++) if(N%i==0){
sum += fixpoint(i) * phi(N/i) ;
}
sum %= MOD ;
LL ans = sum * inv(N) % MOD ;
cout<<ans<<endl ;
return 0;
}
M SPOJ TRANSP Transposing is Fun