HDU - 5812 Distance 数学 + 思维

54 篇文章 0 订阅
16 篇文章 0 订阅

传送门:HDU 5812

题意:给出一个空集合和三个操作。操作I向集合中插入元素X,操作D删除集合中的元素X,操作Q,查询集合中的所有元素与X的最小距离是多少? 
定义最小距离 d(x,y) 为从x变为y只通过乘或者除素数所需要的最少操作次数。例如:d(15,50)=3,因为 15/3×2×5=50

思路:首先解题的关键是要知道:  d(x,y)=f(x/gcd(x,y))+f(y/gcd(x,y))  

其实这一点仔细想想也不是很难理解。

然后考虑如何进行查询操作,很显然我们要枚举x的约数i(也就是gcd(x,y)),然后求集合中所有i的倍数y的f(y / i)的最小值,f[]数组我们可以预处理出来,集合的话我们可以用mulset s[i]  表示i的倍数的f值,然后每次用i的时候从s[i]里取出第一个来就行了。


更快的方法是维护一个数组:用C[y][s]表示当前集合中y的所有倍数z中使得f(z/y)=s的z的数量. 因为s的值不会超过20,所以可以用位压缩的方法,用D[y]表示C[y][]中哪些s值出现了,这样查询最小的s值可以通过位运算快速求出. 插入和删除x时同样可以通过枚举x约数的方法来更新C[y][s]和D[y]的值. 

代码:

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int MAXN = 1000010;
int cnt[MAXN];//cnt[i] := i的质因子个数
bool isprime[MAXN];
void init()
{
	int tmp;
	memset(isprime, 1, sizeof(isprime));
	isprime[0] = isprime[1] = 0;
	for(int i = 2; i < MAXN; i++)
	{
		if(!isprime[i]) continue;
		cnt[i]++;
		for(int j = i + i; j < MAXN; j += i)
		{
			isprime[j] = 0;
			tmp = j;
			while(tmp % i == 0)
			cnt[j]++, tmp /= i;
		}
	}
}
multiset<int> s[MAXN];
multiset<int>::iterator it;
bool book[MAXN];
void add(int i, int x)
{
	s[i].insert(x);
}
void del(int i, int x)
{
	it = s[i].find(x);
	if(it != s[i].end())
	s[i].erase(it);
}
int query(int i)
{
	if(s[i].empty()) return inf;
	return *s[i].begin();
}
int main()
{
	int x, y, num, q, ans, i, kase = 1;
	char ch;
	init();
	while(scanf("%d", &q), q)
	{
		num = 0;
		memset(book, 0, sizeof(book));
		for(i = 0; i < MAXN; i++) s[i].clear();
		printf("Case #%d:\n", kase++);
		while(q--)
		{
			scanf(" %c %d", &ch, &x);
			if(ch == 'I' && !book[x])
			{
				book[x] = 1; num++;
				for(i = 1; i * i < x; i++)
				{
					if(x % i) continue;
					add(i, cnt[x / i]);
					add(x / i, cnt[i]);
				}
				if(i * i == x) add(i, cnt[i]);
			}
			if(ch == 'D' && book[x])
			{
				book[x] = 0; num--;
				for(i = 1; i * i < x; i++)
				{
					if(x % i) continue;
					del(i, cnt[x / i]);
					del(x / i, cnt[i]); 
				}
				if(i * i == x) del(i, cnt[i]);
			}
			if(ch == 'Q')
			{
				if(num == 0)
				{
					printf("-1\n"); continue;
				}
				ans = inf;
				for(i = 1; i * i <= x; i++)
				{
					if(x % i) continue;
					ans = min(ans, query(x / i) + cnt[i]);
					ans = min(ans, query(i) + cnt[x / i]);
				}
				printf("%d\n", ans == inf ? -1 : ans);
			}
		}
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值