[luogu]P2561 [AHOI2002]黑白瓷砖
题意
用六边形瓷砖拼成一个三角形,第i行有i个小瓷砖,问本质不同的方案数有多少。
两个方案本质不同指两个方案不能通过120度或者1270度旋转,或者通过对角线翻转变成另一种。
题解
第二次做\(Burnside\)引理的题目(其实是因为不会\(Polya\))
这道题的置换群比较直观,每个操作都是一个置换。
先对于旋转操作,发现所有的循环置换的循环节长度都为3,对于一个循环置换来说,显然想要构成不动点就必须参与置换的砖块颜色必须一样。
我们考虑有多少个这样的循环节,可以递推处理。
对于一个三角形,考虑最外一层,假如为\(n\)层,则有\(n - 1\)个循环节。
消除最外层之后,里面有\(g[n - 3]\)个循环节。
\(g[1] = 0, g[2] = 1, g[3] = 2\)
考虑完循环节之后发现三角形内部有一个特殊的点,也就是有可能中间有一个不会动的点,循环节长度为1.
这个也可以递推解决。
\(f[1] = 1, f[2] = 0, f[3] = 0\)
\(f[i] = f[i - 3]\)。
然后考虑翻转对称。
对于一个翻转,对称线上的砖块可以任意取色,而不在对称线上的两边必须同色。
可以计算出这样的不动点个数有\(2^{\frac{n + \lfloor \frac{n + 1}{2} \rfloor}{2}}\)个。
然后是单位元。
显然有\(2^{\frac{n \times (n + 1)}{2}}\)个。
根据\(Burnside\)引理,总的方案数为\(2^ {\frac{ 2^{\frac{n \times (n + 1)}{2}} + 2^{\frac{n + \lfloor \frac{n + 1}{2} \rfloor}{2}} \times 3 + 2 ^ {g[n] + f[n]} \times 2}{6}}\)
记得加上高精度。
博客的公式编辑器好像有点问题,建议观察代码获得公式。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <queue>
#include <set>
#include <map>
#include <cstring>
#define int long long
#define Mid ((l + r) >> 1)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
using namespace std;
int read(){
char c; int num, f = 1;
while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
return f * num;
}
struct bignum{
int g[505];
int l;
void init() { l=0; memset(g,0,sizeof(g)); }
void one() { init(); l=1; g[1]=1; }
void jw(){ while(g[l+1]>0) { ++l; g[l+1]=g[l]/10; g[l]%=10; } }
void out() { for(int i=l;i>=1;i--) printf("%d",g[i]); printf("\n"); }
};
int n, g[50], f[50];
bignum ans;
bignum operator +(bignum a,bignum b){
bignum c; c.init();
c.l=max(a.l,b.l);
for(int i=1;i<=c.l;i++){
c.g[i]+=a.g[i]+b.g[i];
c.g[i+1]+=c.g[i]/10;
c.g[i]%=10;
}
c.jw();
return c;
}
bignum operator *(bignum a,bignum b){
bignum c;
c.init(); c.l=a.l+b.l-1;
for(int i=1;i<=a.l;i++)
for(int j=1;j<=b.l;j++)
c.g[i+j-1]+=a.g[i]*b.g[j];
for(int i=1;i<=c.l;i++){
c.g[i+1]+=c.g[i]/10;
c.g[i]%=10;
}
c.jw();
return c;
}
bignum operator /(bignum a,int b){
for(int i=a.l;i>=1;i--){
a.g[i-1]+=(a.g[i]%b)*10;
a.g[i]/=b;
}
while(a.l>1&&a.g[a.l]==0) --a.l;
return a;
}
bignum ksm(int y){
bignum ret; ret.one();
bignum x; x.one(); x.g[1]=2;
while(y){
if(y&1) ret=ret*x;
y>>=1;
x=x*x;
}
return ret;
}
signed main()
{
n = read();
f[1] = 1; f[4] = 1;
g[2] = 1; g[3] = 2; g[4] = 3;
for(int i = 5; i <= 20; i++)
g[i] = g[i - 3] + i - 1,
f[i] = f[i - 3];
ans = ksm(g[n] + 1 + f[n]);
ans = ans + ksm((n * (n + 1) / 2 + (n + 1) / 2) / 2) + ksm((n * (n + 1) / 2 + (n + 1) / 2) / 2) + ksm((n * (n + 1) / 2 + (n + 1) / 2) / 2);
ans = ans + ksm((n * (n + 1) / 2));
ans = ans / 6;
ans.out();
return 0;
}