2016多校联合训练#7
HDU 5812 Distance
数论,数据结构,编程能力题
传送门:HDU
题意
一个集合,初始空,支持三种操作。操作I:向集合中插入元素X,操作D:删除集合中的元素X,操作Q,查询集合中所有元素与给定元素的最小距离最小是多少。定义最小距离 d(x,y) 为从x变为y只通过乘或者除素数所需要的最少操作。例如:d(15,50)=3,因为 15/3×2×5=50
思路
先膜大神:膜膜膜。
在膜大神:膜膜膜。
看了好多博客,上面两只大神是非常有助于我个人理解的两篇,故膜之。
然后说说自己的理解。后三条比较难以理解,可以单步一下。
首先要知道距离的含义,ab的距离就是a和b除去公约数以后剩余因子的个数和。比如15和18,变换方式是 15∗2∗35=18 ,因为15和18约掉公约数3以后是5和6,除去15剩下的因子在乘18应该有的因子才能让15变成18。即:
d(a,b)=f(a/gcd(a,b))+f(b/gcd(a,b)),这个f(x)表示x的素因数个数,可以用线性筛预处理出来100W以内的所有f。用一个集合(set)保存与删除元素。insert函数,count函数,erase函数。
查询一个数m时,枚举它的所有因子ti(不是质因子!),查询集合中存在元素是ti倍数的数k对应的f[k/ti]值,再加上f[m/ti],就是结果。
发现f值永远小于20,因为 220=1048576 ,所以开个二维数组g,记录集合中现有元素,g[i][j]表示集合中某元素m有因子i,且f[m/i]=j,这样的m元素的个数。insert时处理一下,delete时在处理一下。
每次查询时,枚举被查询数m的因子ti,在枚举j=1到20,如果g[ti][j]>0说明集合里有符合条件的元素,集合中那个符合条件的数的抛去公因数以外剩余因数的个数是j,加上f[m/ti]就是结果,结果不断取最小的就是答案。复杂度: O(q∗x√∗20) 。
建议单步一下,理解g数组的作用。
代码
#include <stdio.h>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <cmath>
#include <stack>
#include <cstdlib>
#include <ctime>
using namespace std;
typedef long long ll;
const int MAXN=1000007;
const double eps=1e-8;
const int oo=2000000007;
int f[MAXN][2];
int g[MAXN][21];
set<int> s;
void pre(void)
{
memset(f,0,sizeof(f));
for(int i=0;i<MAXN;i++) f[i][0]=i;
for(int i=2;i<MAXN;i++)
{
if(f[i][1]<=1)
{
for(int j=i;j<MAXN;j+=i)
{
while(f[j][0]%i==0)
{
f[j][0]/=i;
f[j][1]++;
}
}
}
}
}
void add(int n,int t)
{
for(int i=1;i<sqrt(n)+1;i++)
{
if(n%i==0)
{
if(i==n/i)
{
g[i][f[n/i][1]]+=t;
}
else
{
g[i][f[n/i][1]]+=t;
g[n/i][f[i][1]]+=t;
}
}
}
}
int main()
{
pre();
int n;
int t=1;
while(~scanf("%d",&n)&&n!=0)
{
printf("Case #%d:\n",t++);
memset(g,0,sizeof(g));
s.clear();
while(n--)
{
char a[20];
int m;
scanf("%s%d",a,&m);
if(a[0]=='I')
{
if(s.count(m)==0)
{
add(m,1);
s.insert(m);
}
}
else if(a[0]=='D')
{
if(s.count(m)!=0)
{
add(m,-1);
s.erase(m);
}
}
else
{
int ans=oo;
if(s.empty())
{
printf("-1\n");
continue;
}
for(int i=1;i<sqrt(m)+1;i++)
{
if(m%i==0)
{
for(int j=0;j<21;j++)
{
if(g[i][j]>0)
{
ans=min(ans,j+f[m/i][1]);
}
if(g[m/i][j]>0)
{
ans=min(ans,j+f[i][1]);
}
}
}
}
printf("%d\n",ans);
}
}
}
}