wannafly 27 D 巧妙求取约数

链接:https://www.nowcoder.com/acm/contest/215/D
来源:牛客网

题目描述

 

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

一个空的可重集合S。
n次操作,每次操作给出x,k,p,执行以下操作:
1、在S中加入x。
2、输出

输入描述:

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

输出描述:

输出对应的结果
示例1

输入

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

输出

复制
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;
}

 



posted @ 2018-10-30 20:28  楼主好菜啊  阅读(253)  评论(0编辑  收藏  举报