蛋糕

题目大意

CJY很喜欢吃蛋糕,于是YJC弄到了一块蛋糕,现在YJC决定和CJY分享蛋糕。
这块蛋糕上有n^2颗葡萄干,排成了一个n*n的点阵,每颗葡萄干互不相同且被编号为1~n^2。YJC决定沿着一条直线把蛋糕切成两份。YJC和CJY都很喜欢吃葡萄干,所以切出的两份蛋糕必须都包含至少一颗葡萄干。同时他们都不希望吃到不完整的葡萄干,所以切的时候不能经过任意一颗葡萄干。CJY喜欢1号葡萄干,所以他选择了包含1号葡萄干的那块蛋糕,而YJC则选择了另一块。
在吃蛋糕之前,CJY想知道有多少种切蛋糕的方案。两种方案是不同的当且仅当存在一颗葡萄干在一种方案中被分给了CJY,在另一种方案中被分给了YJC。CJY发现自己不会做这道题,所以他来向你请教。这个答案可能很大,你只需要告诉他答案mod p的结果就可以了。

不会推结论
抄了std的式子
然后杜教筛啊

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=1000000+10;
ll sum[maxn][3],gg[maxn][3];
int phi[maxn],pri[maxn];
bool bz[maxn];
int i,j,k,l,t,n,m,top,mo,ans;
void prepare(){
    phi[1]=1;
    fo(i,2,maxn-10){
        if (!bz[i]) pri[++top]=i,phi[i]=i-1;
        fo(j,1,top){
            if ((ll)i*pri[j]>maxn-10) break;
            bz[i*pri[j]]=1;
            if (i%pri[j]==0){
                phi[i*pri[j]]=phi[i]*pri[j];
                break;
            }
            phi[i*pri[j]]=phi[i]*(pri[j]-1);
        }
    }
    fo(i,1,maxn-10){
        sum[i][0]=(sum[i-1][0]+phi[i])%mo;
        sum[i][1]=(sum[i-1][1]+(ll)phi[i]*i%mo)%mo;
        sum[i][2]=(sum[i-1][2]+(ll)phi[i]*i%mo*i%mo)%mo;
    }
}
int getsum(int n,int k){
    int a,b,c;
    if (!k) return n;
    else if (k==1) return ((ll)n*(n+1)/2)%mo;
    else if (k==2){
        a=n;b=n+1;c=2*n+1;
        if (a%2==0) a/=2;else b/=2;
        if (a%3==0) a/=3;
        else if (b%3==0) b/=3;else c/=3;
        return (ll)a*b%mo*c%mo;
    }
    else{
        a=((ll)n*(n+1)/2)%mo;
        return (ll)a*a%mo;
    }
}
int calc(int n,int k){
    if (n<=maxn-10) return sum[n][k];
    if (gg[m/n][k]) return gg[m/n][k];
    int i=2,j,t,s0,s1,s2;
    s0=getsum(n,1);
    s1=getsum(n,2);
    s2=getsum(n,3);
    while (i<=n){
        j=n/(n/i);
        t=(getsum(j,0)-getsum(i-1,0))%mo;
        (s0-=(ll)t*calc(n/i,0)%mo)%=mo;
        t=(getsum(j,1)-getsum(i-1,1))%mo;
        (s1-=(ll)t*calc(n/i,1)%mo)%=mo;
        t=(getsum(j,2)-getsum(i-1,2))%mo;
        (s2-=(ll)t*calc(n/i,2)%mo)%=mo;
        i=j+1;
    }
    gg[m/n][0]=s0;
    gg[m/n][1]=s1;
    gg[m/n][2]=s2;
    return gg[m/n][k];
}
int main(){
    freopen("cake.in","r",stdin);freopen("cake.out","w",stdout);
    scanf("%d%d",&n,&mo);
    m=n;
    prepare();
    ans=(ll)n*n%mo*2%mo*calc(n,0)%mo;
    (ans-=(ll)3*n%mo*calc(n,1)%mo)%=mo;
    (ans+=calc(n,2))%=mo;
    ans=ans*2%mo;
    (ans+=mo)%=mo;
    printf("%d\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值