HDU5812(2016多校第七场)——Distance(数论,枚举)

质因数距离问题解析
本文解析了一种基于质因数的整数距离定义,并提供了解决方案,通过高效的算法处理一系列数学运算,如乘除法,以实现特定整数集合上的插入、删除和查询操作。

Distance

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


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
 


赛后把题解看了一下,大概懂了意思。

但是自己的代码还是没写出来。

把题解和标程贴一下,供大家一起学习。等我过了再把自己的代码也贴上。

————————————————————————————————————————————————————————————————————————————

不难发现d(a, b) = f(a/gcd(a, b)) + f(b/gcd(a,b)),其中f(x)表示x的质因子个数. 因而当遇到操作Q x时,我们只需要枚举x的每个约数y,看属于当前集合的y的所有倍数z中f(z/y)的最小值为多少. 为了快速求出这个最小值,我们用C[y][s]表示当前集合中y的所有倍数z中使得f(z/y)=s的z的数量. 因为s的值不会超过20,所以可以用位压缩的方法,用D[y]表示y的倍数中哪些s值出现了,这样查询最小的s值可以通过位运算快速求出(因为时限是标程的3倍,所以也不会特意卡掉其它方法). 插入和删除x时同样可以通过枚举x约数的方法来更新C[y][s]和D[y]的值. 设M表示元素的最大值,因为1到M所有约数的数量是O(MlogM)的,所以算法的时间和空间复杂度也都是O(MlogM)的. 又因为操作数少于M,所以实际情况还会更好一些.


#include <cstdio>
#include <cstring>
#include <utility>
#include <vector>

using namespace std;

const int N = 100000 + 10;
const int M = 1000000;
const int inf = 1000000000;

vector<int> V[M+1];
vector< pair<int, int> > F[M+1];
int cnt[M+1], sf[M+1], tb[(1<<21)+1], tmp[M+1], fresh[M+1];
bool flag[M+1];
int D[M+1];
int C[M+1][21];

void init()
{
	int i, j;
	for (i=1; i<=M; i++) sf[i] = i;
	for (i=2; i*i<=M; i++) if (sf[i] == i) for (j=i*i; j<=M; j+=i) if (sf[j] == j) sf[j] = i;
}

int decomp(int x)
{
	int s, y, f, i;
	if (cnt[x] != -1) return cnt[x];
	y = x;
	cnt[y] = 0;
	while (x > 1)
	{
		f = sf[x];
		s = 0;
		do
		{
			x /= sf[x];
			s ++;
		}while (x > 1 && sf[x] == f);
		F[y].push_back(make_pair(f, s));
		cnt[y] += s;
	}
	return cnt[y];
}

void search(int x, int k, int f)
{
	if (k == F[x].size()) V[x].push_back(f);
	else
	{
		search(x, k + 1, f);
		for (int i=1; i<=F[x][k].second; i++)
		{
			f *= F[x][k].first;
			search(x, k + 1, f);
		}
	}
}

void cal(int x)
{
	if (V[x].size()) return;
	decomp(x);
	search(x, 0, 1);
}

int main()
{
	int res, q, cn, x, y, z, top, i;
	char cmd;
	memset(cnt, -1, sizeof(cnt));
	init();
	for (i=0; i<21; i++) tb[1<<i] = i;
	cn = 0;
	while (scanf("%d\n", &q), q > 0)
	{
		printf("Case #%d:\n", ++ cn);
		memset(flag, false, sizeof(flag));
		top = 0;
		while (q --)
		{
			scanf("%c %d\n", &cmd, &x);
			if (cmd == 'I')
			{
				if (!flag[x])
				{
					flag[x] = true;
					cal(x);
					for (i=0; i<V[x].size(); i++)
					{
						y = V[x][i];
						if (fresh[y] != cn)
						{
							tmp[top++] = y;
							fresh[y] = cn;
						}
						int s = decomp(x / y);
						C[y][s] ++;
						if (C[y][s] == 1) D[y] ^= (1<<s);
					}
				}
			}
			else if (cmd == 'D')
			{
				if (flag[x])
				{
					flag[x] = false;
					for (i=0; i<V[x].size(); i++)
					{
						y = V[x][i];
						int s = decomp(x / y);
						C[y][s] --;
						if (C[y][s] == 0) D[y] ^= (1<<s);
					}
				}
			}
			else
			{
				cal(x);
				res = inf;
				for (i=0; i<V[x].size(); i++)
				{
					y = V[x][i];
					if (!D[y]) continue;
					z = tb[D[y]&(-D[y])] + decomp(x / y);
					if (z < res) res = z;
				}
				if (res == inf) printf("-1\n"); else printf("%d\n", res);
			}
		}
		for (i=0; i<top; i++) 
		{
			D[tmp[i]] = 0;
			memset(C[tmp[i]], 0, sizeof(C[tmp[i]]));
		}
	}
	return 0;
}










评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值