1009 绿魔法师 容斥 鸽巢原理 gcd

链接:https://ac.nowcoder.com/acm/contest/26656/1009
来源:牛客网

题目描述

 

“我不知道你在说什么,因为我只是个pupil。”--绿魔法师

一个空的可重集合S。
n次操作,每次操作给出x,k,p,执行以下操作:
1、在S中加入x。
2、输出∑y∈Sgcd(x,y)k(modp)\sum_{y \in S}{gcd(x,y)^k} (mod p)ySgcd(x,y)k(modp)。

输入描述:

所有输入的数都是小于1e5+1的正整数。

输出描述:

输出对应的结果
示例1

输入

复制
3
4 1 9
5 2 8
6 3 7

输出

复制
4
2
1

 

分析

求调和级数是nlnn

看了两天,在阅读大量题解,然后问了同学后,仔细想了一段时间后,明白了。。。

(x,x1)^k + (x,x2)^k + (x,x3)^k + (x,x4)^k + (x,x5)^k.......

只要把所有的(x,y)相等且等于d的数量得到 就可以化简成 -> d1 ^ k * cnt(d1) + d2 ^ k * cnt(d2) ...........

把每个因子的数量存在num[x] 里,表示这个因子的数量

从大到小枚举当前的x 的因子 ,如果数量大于0,就按推的公式计算一下

最后由于容斥原理,让这个数的因子在计数的时候不要计入这个数的数量。

ps:题意就是每个数加进去的时候就要和本身算一次最大公约数。。。所以一开始就 cnt[v[x][i]] += num[v[x][i]] 是没问题的,希望以后读题要更严谨。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=1e5+10;
ll num[N];
ll cnt[N];

vector<ll>v[N];

ll qmi(ll a,ll b,ll M)
{
    if(b==0)return 1;
    else if(b%2==1)return qmi(a*a%M,b/2,M)%M*a%M;
    else return qmi(a*a%M,b/2,M)%M;
}

int main()
{
    for(int i=1;i<=N;i++){
        int d=i;
        while(d<=N){
            v[d].push_back(i);
            d+=i;
        }
    }
    ll t;
    cin>>t;
    while(t--){
        ll x,k,p;
        cin>>x>>k>>p;
        ll res=0;
        for(auto it:v[x])num[it]++;
//         for(auto it:v[x])dbb(it,num[it]);
//         cout<<endl;
        for(int i=v[x].size()-1;i>=0;i--){
            cnt[v[x][i]]+=num[v[x][i]];
//             dbb(cnt[v[x][i]],v[x][i]);
            if(cnt[v[x][i]]>0)res+=qmi(v[x][i],k,p)%p*cnt[v[x][i]]%p;
            else continue;
            for(auto it:v[v[x][i]]){
                cnt[it]-=cnt[v[x][i]];
            }
//             dbb(cnt[v[x][i]],v[x][i]);
//             cnt[v[x][i]]=0;
            res%=p;
        }
        cout<<res<<'\n';
    }
}

 

posted @ 2022-07-26 03:52  er007  阅读(41)  评论(0编辑  收藏  举报