jzoj5347. 【NOIP2017提高A组模拟9.5】遥远的金字塔 容斥

11 篇文章 0 订阅

题意:给出n,m,总共有n+1个位置,第n+1个位置上固定为m,要求在前n个位置上填不大于m的正整数,然后在数轴上的0点有一个球,第i次走的步数可以是第i个位置上的数或者其相反数(后退)。要求最后能走到1.问有多少种方法。

我仿佛是个丝薄。。明明这题更水偏偏要去搞DP。。
可以手玩或者通过扩欧证明得一个合法的序列一定他的总gcd=1。
那么我们可以发现,要求总序列gcd!=1,肯定是整个数列都是m的质因数的倍数
枚举m的质因数x,那么1-m中有m/x个x的倍数,减去就好了。
问题是会减重,这怎么办?当然是容斥大法好啊。
2^tot次方枚举每个质因数是否选择,偶加奇减。可能这样子有点模糊我举个例子。
假设我现在是n=2,m=6
x=2,3。
那么我不选择任何质因数的时候就是总方案数,m^n.
选择两个质因数的贡献是(m/2/3)^2,这里的2次方是因为有两个数,题解中说的有些歧义= =,实际上是每个位置我们都可以取当前m/总乘积这么多个数,所以总共n个位置方案数当然是(m/总乘积)^tmp。
细节的话dfs也可以,直接枚举二进制状态也可以,常数上明明应该dfs大一点,但是inline全开以后差不多= =

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int N=1e5+5;
const int mo=1e9+7;
ll n,m,p[N],tot;
ll ans;
inline ll pow(ll a,ll b)
{
    ll ret=1;
    while (b)
    {
        if (b&1)ret=1ll*ret*a%mo;
        a=1ll*a*a%mo;
        b>>=1;
    }
    return ret%mo;
}
inline void dfs(int x,int count,ll mul)
{
    if (x==tot+1)
    {
        int c=count%2==1?-1:1;
        ans=(ans+1ll*c*pow(m/mul%mo,n)%mo+mo)%mo;
        return;
    }
    dfs(x+1,count,mul);
    dfs(x+1,count+1,mul*p[x]);
}
int main()
{
    freopen("heal.in","r",stdin);
    freopen("heal.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    ll k=m;
    for(int i=2;i*i<=k;i++)
    {
        if (k%i==0)
        {
            p[++tot]=i;
            while (k%i==0)k/=i;
        }
    }if (k>1)p[++tot]=k;
    //ans=pow(m,n);
    dfs(1,0,1);
    printf("%lld\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值