hdu2865 Birthday Toy(置换+矩阵优化dp)

题目链接

分析:
首先中间的大珠子需要一种颜色,而这种颜色是不能为周围珠子使用的:m=k-1

如果没有“相邻颜色不同”这个限制,这道题就和poj2154一样了

poj2154

旋转i个珠子,轮换个数就是 gcd(n,i) g c d ( n , i )
不动点数: mgcd(n,i) m g c d ( n , i )
ans=1nni=1mgcd(i,n) a n s = 1 n ∑ i = 1 n m g c d ( i , n )

ni=1gcd(i,n) ∑ i = 1 n g c d ( i , n )
=ni=1d|nd|iphi(d) = ∑ i = 1 n ∑ d | n ∑ d | i p h i ( d )
=ni=1d|nd|iphi(d) = ∑ i = 1 n ∑ d | n ∑ d | i p h i ( d )
=d|nndphi(d) = ∑ d | n n d p h i ( d )

ans=1nni=1phi(nd)md a n s = 1 n ∑ i = 1 n p h i ( n d ) ∗ m d

如果有了相邻不同的限制,相邻的轮换就应该染不同色
显然,只要满足长度 gcd(n,i) g c d ( n , i ) 的一段相邻颜色不同整个环就不同了
(这 gcd(n,i) g c d ( n , i ) 个元素一定属于不同的轮换)

把轮换视为一个点,那我们就对这 gcd(n,i) g c d ( n , i ) 个点染色
设计状态: f[i] f [ i ] 表示给一个 i i 个点环上色,使得相邻不同色的方案数

转移:f[i]=(m2)f[i1]+(m1)f[i2]

f[1]=m,f[2]=m(m1),f[3]=m(m1)(m2) f [ 1 ] = m , f [ 2 ] = m ∗ ( m − 1 ) , f [ 3 ] = m ∗ ( m − 1 ) ∗ ( m − 2 )

一开始我怎么也不明白这个式子,想了好久:
我们把环从第 i i 个点劈开,把环变成序列,这样队首就是i i1 i − 1 是队尾
队尾和队首的颜色不能相同,且 i i 不能与i+1相同,所以 (m2)f[i1] ( m − 2 ) ∗ f [ i − 1 ]
但是 i i 可以和i2颜色一样: (m1)f[i2] ( m − 1 ) ∗ f [ i − 2 ]

对于任意一个置换,不动点的数目: f(gcd(n,i)) f ( g c d ( n , i ) )
用矩阵是很容易计算的

ans=knni=1f(gcd(n,i)) a n s = k n ∑ i = 1 n f ( g c d ( n , i ) )

利用poj2865的奇技淫巧:

ans=knd|nphi(nd)f(d) a n s = k n ∑ d | n p h i ( n d ) f ( d )

注意:这里有一个小限制,当d=1时(即只有一个轮换),上式值为0

tip

dp想不出来。。。一开始只是简单的认为是 m(m1)(m1)...(m2) m ∗ ( m − 1 ) ∗ ( m − 1 ) ∗ . . . ∗ ( m − 2 )

至于最后的奇技淫巧,觉得就是计算 f(i) f ( i ) 的累积次数,从式子上看: d|ndphi(nd) ∑ d | n d ∗ p h i ( n d )
转化之后, phi(nd) p h i ( n d ) 是不变的,函数操作加在 d d <script type="math/tex" id="MathJax-Element-4050">d</script>上即可

#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long

using namespace std;

const int N=35000;
const ll p=1e9+7;
ll n,m,k,f1,f2,f3;
int sshu[N],tot=0;
bool no[N];
struct node{
    ll H[2][2];
    node operator *(const node &a) const 
    {
        node ans;
        for (int i=0;i<2;i++)
            for (int j=0;j<2;j++)
            {
                ans.H[i][j]=0;
                for (int k=0;k<2;k++)
                    ans.H[i][j]=(ans.H[i][j]+H[i][k]*a.H[k][j]%p)%p;
            }
        return ans;
    }
    node KSM(ll b)
    {
        node ans=(*this),a=(*this);
        b--;
        while (b)
        {
            if (b&1) ans=ans*a;
            b>>=1;
            a=a*a;
        }
        return ans;
    }
};
node H,A;

void prepare()
{
    for (int i=2;i<N;i++)
    {
        if (!no[i]) sshu[++tot]=i;
        for (int j=1;j<=tot&&sshu[j]*i<N;j++)
        {
            no[sshu[j]*i]=1;
            if (i%sshu[j]==0) break;
        }
    }
}

ll phi(ll x)
{
    ll ans=x;
    for (int i=1;i<=tot&&sshu[i]*sshu[i]<=x;i++) if (x%sshu[i]==0)
    {
        ans=ans/sshu[i]*(sshu[i]-1);
        while (x%sshu[i]==0) x/=sshu[i];
    }
    if (x>1) ans=ans/x*(x-1);
    return ans;
}

ll KSM(ll a,ll b)
{
    ll t=1; a%=p;
    while (b)
    {
        if (b&1) t=(t*a)%p;
        b>>=1;
        a=(a*a)%p;
    }
    return t%p;
}

ll F(ll x)
{
    if (x==1) return f1;
    if (x==2) return f2;
    if (x==3) return f3;
    A=H.KSM(x-3);
    return (A.H[0][0]*f3%p+A.H[1][0]*f2%p)%p;
}

int main()
{
    prepare();
    while (scanf("%lld%lld",&n,&k)!=EOF)
    {
        m=k-1; 
        f1=m%p; f2=m%p*(m-1)%p; f3=m%p*(m-1)%p*(m-2)%p;
        H.H[0][0]=m-2; H.H[0][1]=1; H.H[1][0]=m-1; H.H[1][1]=0;

        ll ans=0;
        for (ll i=1;i*i<=n;i++) if (n%i==0)
        {
            ll x=i,y=n/i;
            if (x!=1) ans=(ans+phi(y)%p*F(x)%p)%p;
            if (x!=y) ans=(ans+phi(x)%p*F(y)%p)%p;
        }
        ans=(ans*k)%p;
        ans=(ans%p*KSM(n,p-2)%p)%p;
        printf("%lld\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值