HUST 1569(Burnside定理+容斥+数位dp+矩阵快速幂)

 

传送门:Gift

题意:由n(n<=1e9)个珍珠构成的项链,珍珠包含幸运数字(有且仅由4或7组成),取区间[L,R]内的数字,相邻的数字不能相同,且旋转得到的相同的数列为一种,为最终能构成多少种项链。

分析:这是我做过的最为综合的一道题目(太渣了),首先数位dp筛选出区间[L,R]内的幸运数字总数,dp[pos]表示非限制条件下还有pos位含有的幸运数字个数,然后记忆化搜索一下,随便乱搞的(直接dfs不知会不会超时,本人做法900+ms险过,应该直接dfs会超时),再不考虑旋转相同的情况,可以dp再构造矩阵,dp[i][0]表示到达第i位时,最后一个珠子与第一个珠子不同,dp[i][1]表示到达第i位最后一个珠子与第一个相同,则状态转移方程为:

dp[i][0]=dp[i-1][0]*(m-2)+dp[i-1][1]*(m-1)(m为幸运数字种类).

dp[i][1]=dp[i-1][0]*1

上面构造矩阵快速幂求出总数后再由Burnside定理+容斥得到答案,这个问题较为常见,可参考poj2888做法。

#include <stdio.h>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <cstdlib>
#define LL long long
#define N 25
#define mod 1000000007
using namespace std;
/**************************矩阵快速幂**************************/
struct matrix
{
    LL m[2][2];
    void init()
    {
        for(int i=0;i<2;i++)
            for(int j=0;j<2;j++)
            m[i][j]=(i==j);
    }
}M;
matrix mult(matrix a,matrix b)
{
    matrix c;
    memset(c.m,0,sizeof(c.m));
    for(int k=0;k<2;k++)
    for(int i=0;i<2;i++)
    {
        if(a.m[i][k]==0)continue;
        for(int j=0;j<2;j++)
        {
            c.m[i][j]=(c.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
        }
    }

    return c;
}
matrix quick_pow(matrix a,int n)
{
    matrix res;
    res.init();
    while(n)
    {
        if(n&1)res=mult(res,a);
        a=mult(a,a);
        n>>=1;
    }
    return res;
}
LL calc(LL n,LL m)
{
    matrix res;
    res.m[0][0]=m-2;res.m[0][1]=m-1;
    res.m[1][0]=1;res.m[1][1]=0;
    res=quick_pow(res,n-1);
  //  printf("m=%lld n=%lld res=%lld\n",m,n,res.m[0][1]);
    return m*res.m[0][1]%mod;
}
/**************************矩阵快速幂**************************/
LL POW(LL a,LL n)
{
    LL res=1;
    a%=mod;
    while(n)
    {
        if(n&1)res=res*a%mod;
        a=a*a%mod;
        n>>=1;
    }
    return res;
}
/**********************容斥**************************/
#define MAXN 12
#define MAXM 32000
#define EPS 1e-8
bool p[MAXM];
vector<int> prime;
vector<int> factor;
vector<int> primefactor;
void Init() {
    int i, j;
    prime.clear();
    memset(p, true, sizeof(p));
    for (i = 2; i < 180; i++) {
        if (p[i]) {
            for (j = i * i; j < MAXM; j += i)
                p[j] = false;
        }
    }
    for (i = 2; i < MAXM; i++) {
        if (p[i])
            prime.push_back(i);
    }
}
void Prime(int x) {
    int i, tmp;
    primefactor.clear();
    tmp = (int) (sqrt((double) x) + EPS);
    for (i = 0; prime[i] <= tmp; i++) {
        if (x % prime[i] == 0) {
            primefactor.push_back(prime[i]);
            while (x % prime[i] == 0)
                x /= prime[i];
        }
    }
    if (x > 1)
        primefactor.push_back(x);
}
int Mul(int x, int &k) {
    int i, ans;
    ans = 1;
    for (i = k = 0; x; x >>= 1, i++) {
        if (x & 1) {
            k++;
            ans *= primefactor[i];
        }
    }
    return ans;
}
LL Phi(int x) {
    int i, j, t, ans, tmp;
    Prime(x);
    ans = 0;
    t = (int) primefactor.size();
    for (i = 1; i < (1 << t); i++) {
        tmp = Mul(i, j);
        if (j & 1)
            ans += x / tmp;
        else
            ans -= x / tmp;
    }
    return (x - ans) %mod;
}
/**********************容斥**************************/
LL solve(LL n,LL m)
{
    LL ans=0;
    for(int i=1;i*i<=n;i++)
    {
        if(n%i==0)
        {
            int a=i,b=n/i;
            if(i*i==n)
            {
                ans=(ans+Phi(n/a)*calc(a,m))%mod;
            }
            else
            {
                ans=(ans+Phi(n/a)*calc(a,m))%mod;
                ans=(ans+Phi(n/b)*calc(b,m))%mod;
            }
        }
    }
    ans=ans*POW(n,mod-2)%mod;
    return ans;
}
/************************数位dp**********************/
LL dig[25],dp[25];
LL dfs(int pos,int flag,int limit,int fzore)
{
    if(pos==0)return flag;
    if(!fzore&&!limit&&~dp[pos])return dp[pos];
    int len=limit?dig[pos]:9;
    LL ans=0;
    for(int i=0;i<=len;i++)
    {
        if(i==4||i==7||i==0){
        if(fzore&&i==0)ans+=dfs(pos-1,0,i==len&&limit,1);
        else if(i==4||i==7)ans+=dfs(pos-1,1,i==len&&limit,0);}
    }
    if(!fzore&&!limit)dp[pos]=ans;
    return ans;
}
LL fun(LL x)
{
    int len=0;
    if(x<4)return 0;
    while(x)
    {
        dig[++len]=x%10;
        x/=10;
    }
    return dfs(len,0,1,1);
}
/************************数位dp**********************/
int main()
{
    LL n,L,R;
    Init();
    memset(dp,-1,sizeof(dp));
    while(scanf("%lld%lld%lld",&n,&L,&R)>0)
    {
        LL num=fun(R)-fun(L-1);
        if(n==0)
        {
            puts("0");continue;
        }
        if(n==1)
        {
            printf("%d\n",num%mod);
            continue;
        }
        printf("%d\n",solve(n,num));

    }
}
View Code

 

转载于:https://www.cnblogs.com/lienus/p/4395196.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值