【HDOJ 5812】Distance(约数拆分)

【HDOJ 5812】Distance(约数拆分)

Distance

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 216    Accepted Submission(s): 81


Problem Description
In number theory, a prime is a positive integer greater than 1 that has no positive divisors other than 1 and itself. The distance between two positive integers x and y, denoted by d(x, y), is defined as the minimum number of multiplications by a prime or divisions (without a remainder) by a prime one can perform to transform x into y. For example, d(15, 50) = 3, because 50 = 15 * 2 * 5 / 3, and you have to perform two multiplications (*2, *5) and one division (/3) to transform 15 into 50.

For a set S of positive integers, which is initially empty, you are asked to implement the following types of operations on S.

1.  I x: Insert x into S. If x is already in S, just ignore this operation.
2.  D x: Delete x from S. If x is not in S, just ignore this operation.
3.  Q x: Find out a minimum z such that there exists a y in S and d(x, y) = z.


Input
The input contains multiple test cases. The first line of each case contains an integer Q (1 <= Q <= 50000), indicating the number of operations. The following lines each contain a letter ‘I’, ‘D’ or ‘Q’, and an integer x (1 <= x <= 1000000).
Q = 0 indicates the end of the input.
The total number of operations does not exceed 300000.


Output
For each case, output “Case #X:” first, where X is the case number, starting from 1. Then for each ‘Q’ operation, output the result in a line; if S is empty when a ‘Q’ operation is to perform, output -1 instead.


Sample Input
  
  
12 I 20 I 15 Q 30 I 30 Q 30 D 10 Q 27 I 15 D 15 D 20 D 30 Q 5 0


Sample Output
  
  
Case #1: 1 0 3 -1


Author
SYSU


Source


题目大意:

定义两个整数x,y之间的距离d(x,y)为x转换为y的操作数,操作是指乘或除素数。
譬如 15转换为50需要 /3  2  5 三种操作。

然后有如下三种操作
I x 表示向当前集合S中加入x,如果集合S存在x,忽略此次操作。
D x 表示删除集合S中的x,如果集合S中不存在x,忽略此次操作。
Q x 表示查询对于集合S中的整数y,d(x,y)的最小值,如果S为空,输出-1。

转换的思想比较巧妙。

把查询和插入的数,都转换为约数。

这样对于每次查询x,可以遍历x的所有约数,此时x到某个约数 y 的距离为cnt[x/y]也就是 x/y 的素因子个数。
对于集合中的每个数x,也一样,到每个约数 y 的距离为cnt[x/y]

因为距离很小,最多20(10^6)内的数,拆分素因子个数不超20
这样可以定义数组 C[y][s] 表示集合S中的所有数,到y的距离为s的数的个数。
这样每次插入一个数x时,遍历x的所有约数y, C[y][cnt[x/y]]++

查询一个数x时,遍历x的所有约数y,查找的就是 min(cnt[x/y]+C[y][s]0s)

然后就是求约数的问题。预处理很爆炸。并且操作也没到 106
所以可以在三种操作过程中拆分。

标称中一个巧妙的做法是把 106 内每个数中的素数存下来到 f[] 中,只存一个素因子就可以,任意一个。

这样查询x的约数时,用x不断除 f[x] ,然后可以求出x中 f[x] 的个数,除后的余数 x 又会有一个新的 f[x] ,这些统计下来都是 x <script type="math/tex" id="MathJax-Element-2447">x</script>中的素因子。

然后求约数用dfs就行,x中的素因子排列组合。

素材都有了,拼接起来就A啦

代码如下:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define fread(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)
//#define Debug

using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 1e6;
const int mod = 1e9+7;
const double eps = 1e-8;

bool Isprim[msz+1];
bool vis[msz+1];
int p[msz+1];
int f[msz+1],cnt[msz+1];
int C[msz+1][22],D[msz+1];
int low[1<<22];
vector <int> ys[msz+1];
vector <Pr> sy[msz+1];
int tp;

void init()
{
    memset(Isprim,0,sizeof(Isprim));
    cnt[1] = 0;
    for(int i = 0; i <= 20; ++i)
        low[1<<i] = i;

    tp = 0;
    for(int i = 2; i <= msz; ++i)
    {
        if(!Isprim[i])
        {
            p[tp++] = i;
            f[i] = i;
            cnt[i] = 1;
        }
        for(int j = 0; j < tp && p[j]*i <= msz; ++j)
        {
            f[p[j]*i] = p[j];
            cnt[p[j]*i] = cnt[p[j]]+cnt[i];
            Isprim[p[j]*i] = 1;
            if(i%p[j] == 0) break;
        }
    }

}

void cal(int x)
{
    int pos = x;
    int c,tmp;
#ifdef Debug
    printf("solve:%d\n",x);
#endif

    while(x > 1)
    {
        c = 0;
        tmp = f[x];

        while(x%tmp == 0)
        {
            x /= tmp;
            c++;
        }
#ifdef Debug
        printf("prim:%d cnt:%d\n",tmp,c);
#endif
        sy[pos].push_back(Pr(tmp,c));
    }
#ifdef Debug
    puts("------");
#endif
}

void solve(int x,int k,int f)
{
    if(k == sy[x].size()) ys[x].push_back(f);
    else
    {
        solve(x,k+1,f);
        for(int i = 0; i < sy[x][k].second; ++i)
        {
            f *= sy[x][k].first;
            solve(x,k+1,f);
        }
    }
}

void Insert(int x)
{
    if(vis[x]) return;
    vis[x] = 1;

    if(!sy[x].size()) cal(x);
    if(!ys[x].size()) solve(x,0,1);
    int y;
#ifdef Debug
    printf("%d:\n",x);
#endif

    for(int i = 0; i < ys[x].size(); ++i)
    {
        y = ys[x][i];
        if(!C[y][cnt[x/y]]) 
        {
            D[y] ^= (1<<cnt[x/y]);
#ifdef Debug
            printf("Change! %d:%d\n",y,D[y]);
#endif
        }

#ifdef Debug
        printf("%d<-%d %d\n",x,y,cnt[x/y]);
#endif
        C[y][cnt[x/y]]++;
    }
#ifdef Debug
    puts("//");
#endif
}

void Delete(int x)
{
    if(!vis[x]) return;
    vis[x] = 0;

#ifdef Debug
    printf("%d:",x);
#endif

    for(int i = 0; i < ys[x].size(); ++i)
    {
        int y = ys[x][i];
#ifdef Debug
        printf("%d ",y);
#endif
        C[y][cnt[x/y]]--;
        if(!C[y][cnt[x/y]]) D[y] ^= (1<<cnt[x/y]);
    }
#ifdef Debug
    puts("//");
#endif
}

int Lowbit(int x)
{
    return x&(-x);
}

int Query(int x)
{
    if(!sy[x].size()) cal(x);
    if(!ys[x].size()) solve(x,0,1);

    int ans = -1;
#ifdef Debug
    printf("Query %d:\n",x);
#endif

    for(int i = 0; i < ys[x].size(); ++i)
    {
        int y = ys[x][i];
        if(!D[y]) continue;
        int tmp = cnt[x/y];

#ifdef Debug
        printf("%d->%d: %d\n",x,y,tmp);
#endif
        tmp += low[Lowbit(D[y])];
#ifdef Debug
        printf("MIN%d: %d\n",y,Lowbit(D[x/y])>>1);
#endif
        if(ans == -1 || ans > tmp) ans = tmp;
    }
#ifdef Debug
    puts("//");
#endif

    return ans;
}

int main()
{
    //fread("");
    //fwrite("");

    init();

    int q,x,z = 1;
    char opt[3];

    while(~scanf("%d",&q) && q)
    {
        memset(vis,0,sizeof(vis));
        memset(C,0,sizeof(C));
        memset(D,0,sizeof(D));
        printf("Case #%d:\n",z++);
        while(q--)
        {
            scanf("%s%d",opt,&x);
            if(opt[0] == 'I') Insert(x);
            else if(opt[0] == 'D') Delete(x);
            else printf("%d\n",Query(x));
        }
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值