1009 绿魔法师 容斥 鸽巢原理 gcd
链接:https://ac.nowcoder.com/acm/contest/26656/1009
来源:牛客网
题目描述
“我不知道你在说什么,因为我只是个pupil。”--绿魔法师
n次操作,每次操作给出x,k,p,执行以下操作:
1、在S中加入x。
2、输出∑y∈Sgcd(x,y)k(modp)\sum_{y \in S}{gcd(x,y)^k} (mod p)∑y∈Sgcd(x,y)k(modp)。
输入描述:
所有输入的数都是小于1e5+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'; } }