wannafly 27 D 巧妙求取约数
链接:https://www.nowcoder.com/acm/contest/215/D
来源:牛客网
题目描述
“我不知道你在说什么,因为我只是个pupil。”--绿魔法师
n次操作,每次操作给出x,k,p,执行以下操作:
1、在S中加入x。
2、输出。
输入描述:
所有输入的数都是小于1e5+1的正整数。
输出描述:
输出对应的结果
示例1
输出
复制4 2 1
题意 : 每次增加一个数,求新增加的数同前面每个数的gcd 的 k 次幂再对 p 取模,将答案累加
思路分析 :
考虑每次增加一个数时,需要找到同前面每个数字的 gcd, 本质上的 gcd 就是当前数的约数中的某一个
因此每插入一个新的元素时,就是相当于插入了当前这个数的所有的约数,从最大的约数开始插入,并且每次检索前面有此约数的数有多少个即可
复杂度 n*w*w ,w 为这个数的约数个数,但实际在 w^2 枚举因数时的复杂度是不够 w^2 的
代码示例 :
#define ll long long const ll maxn = 1e5+5; ll n; ll x, k, p; vector<ll>ve[maxn]; inline ll read() { ll x = 0, f = 1; register char ch = getchar(); while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); } while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x*f; } inline void init() { for(ll i = 1; i <= 100000; i++){ for(ll j = i; j <= 100000; j += i){ ve[j].push_back(i); } } } inline ll qw(ll x, ll cnt){ ll res = 1; while(cnt){ if (cnt&1) res *= x; res %= p; x *= x; x %= p; cnt >>= 1; } return res; } ll cnt[maxn], del[maxn]; ll pp[maxn], pt[maxn]; inline void solve() { for(ll i = 0; i < ve[x].size(); i++) cnt[ve[x][i]]++; ll ans = 0; ll c = 0; for(ll i = ve[x].size()-1; i >= 0; i--){ ll num = ve[x][i]; if (cnt[num] <= del[num]) continue; ans += (cnt[num]-del[num])*qw(num, k)%p; ans %= p; for(ll j = 0; j < ve[num].size(); j++){ ll f = ve[num][j]; del[f] += (cnt[num]-del[num]); } } for(int i = 0; i < ve[x].size(); i++) del[ve[x][i]] = 0; printf("%lld\n", ans); } int main() { init(); cin >> n; for(ll i = 1; i <= n; i++){ x = read(), k = read(), p = read(); solve(); } return 0; }
东北日出西边雨 道是无情却有情