这道题应该算是那次校赛时最难的一道题了吧,是唯一一道没人做出来的题目,不过,在经历了这么多题海的洗礼之后,我发现其实这道题并不是那么的难,也是套路。先说说当时的悲惨经历,看到之后认为就是一道简单的状压dp,所以写了一个矩阵快速幂,结果发现对不上答案,仔细一想才发现有无数重复的情况,并且这些重复和手环的最小循环节有关,这样我一下子就不知所措了,我在之前的文章里也写到过可能是有一种比较高明的方法去重,事实证明的确是这样,在学习了反演和polya定理之后,才发现去重其实只是一个模板的问题,这样这道题其实是两部分,首先算出长度为k的不可旋转时候的种类数,也就是一个状压dp,以及使用polya模板去重,由于我保留了之前比赛的时候写的代码(比完赛考代码是一个好习惯),我(之前的那个)也算是不孚众望,把前一个部分写对了,所以前一个部分就直接拷贝了,后一个部分就是贴模板的事,不过当我满心欢喜地以为把这道题秒杀的时候,我才发现一个更坑的问题——超时,这道题只给了1s,我用了一些邪术开大了这道题的时间之后发现我写的长度为8矩阵快速幂要4s,所以我做了两个优化,第一因为底矩阵是恒定的,所以我直接打表打出了每个2的幂次矩阵,这样就少了一大半(因为每次ans不一定乘a)的计算,然而还是超了300ms,之后我手足无措了,只好打表找规律,我打了一个矩阵的次方,发现某些位永远是0,所以我就跳过了一些为的计算,这样总算是可以了。
总结一下,这道题还是可以的,不过现在看来远没有当初想的那么艰难,把两部分想清楚的话其实是挺简单的,看来还是积累的知识不够导致的,经历了这些天的多校赛,我才发现真的是人外有人,天外有天,看来修炼是永远无法停歇的。
贴个代码吧
# include <cstdio>
# include <cstring>
# include <algorithm>
using namespace std;
const int Mod = 1e9 + 7;
typedef long long ll;
const int MAX_N = 1000;
struct mat
{
ll val[8][8];
};
mat zz[30];
mat t = {
{
{0 , 1 , 0 , 0 , 1 , 0 , 0 , 1} ,
{1 , 0 , 0 , 0 , 0 , 0 , 1 , 0} ,
{0 , 0 , 0 , 0 , 0 , 1 , 0 , 0} ,
{0 , 0 , 0 , 0 , 1 , 0 , 0 , 0} ,
{1 , 0 , 0 , 1 , 0 , 0 , 0 , 0} ,
{0 , 0 , 1 , 0 , 0 , 0 , 0 , 0} ,
{0 , 1 , 0 , 0 , 0 , 0 , 0 , 0} ,
{1 , 0 , 0 , 0 , 0 , 0 , 0 , 0}
}
};
void mul(mat A , mat B , mat &D)
{
mat C = {0};
int i, j, k;
if(B.val[0][0])
{
for(i = 0 ; i < 8 ; i++)
{
for(j = i % 3 ; j < 8 ; j += 3)
{
for(k = i % 3 ; k < 8 ; k += 3)
{
C.val[i][j] += A.val[i][k] * B.val[k][j];
C.val[i][j] %= Mod;
}
}
}
memcpy(D.val , C.val , sizeof(C.val));
return;
}
for(i = 0 ; i < 8 ; i++)
{
for(j = 0 ; j < 8 ; j++)
{
for(k = 0 ; k < 8 ; k++)
{
C.val[i][j] += A.val[i][k] * B.val[k][j];
C.val[i][j] %= Mod;
}
}
}
memcpy(D.val , C.val , sizeof(C.val));
}
mat po(ll n)
{
mat B = {0};
int i;
for(i = 0 ; i < 8 ; i++)
B.val[i][i] = 1;
i = 0;
while(n)
{
if(n & 1)
mul(B , zz[i] , B);
n >>= 1;
i++;
}
return B;
}
ll sea(int n)
{
ll ans = 0;
mat A = po(n);
int i;
for(i = 0 ; i < 8 ; i++)
ans += A.val[i][i];
return ans % Mod;
}
int N;
int box[MAX_N];
int num;
int st[MAX_N];
int top;
int po(int a , ll b , int mo)
{
int ans = 1;
while(b)
{
if(b & 1)
ans = (ll)ans * a % mo;
b >>= 1;
a = (ll)a * a % mo;
}
return ans;
}
void div(int n)
{
num = top = 0;
box[num++] = 1;
int i, j;
for(i = 2 ; (ll)i * i <= n ; i++)
{
if(n % i == 0)
{
int siz = num;
st[top++] = i;
while(n % i == 0)
{
n /= i;
for(j = 0 ; j < siz ; j++)
{
box[num] = box[num - siz] * i;
num++;
}
}
}
}
if(n - 1)
{
int siz = num;
st[top++] = n;
for(j = 0 ; j < siz ; j++)
{
box[num] = box[num - siz] * n;
num++;
}
}
}
void polya()
{
div(N);
int i, j;
ll res = 0;
for(i = 0 ; i < num ; i++)
{
ll euler = box[i];
for(j = 0 ; j < top ; j++)
{
if(box[i] % st[j] == 0)
euler = euler / st[j] * (st[j] - 1);
}
res = (res + euler % Mod * sea(N / box[i])) % Mod;
}
printf("%lld\n", res * po(N , Mod - 2 , Mod) % Mod);
}
int main()
{
int i;
zz[0] = t;
int j, k;
for(i = 1 ; i < 30 ; i++)
{
mul(zz[i - 1] , zz[i - 1] , zz[i]);
}
while(~scanf("%d", &N))
{
polya();
}
return 0;
}
版权声明:本文为qq_40772738原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。