HDU - 5812 Distance 数学 + 思维
2017-09-16 21:50
246 查看
传送门: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);
}
}
}
}
题意:给出一个空集合和三个操作。操作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);
}
}
}
}
相关文章推荐
- HDU 5178 pairs —— 思维 + 二分
- 【HDU-4004 】The Frog's Games 【二分+思维】
- HDU_6127 Hard challenge 【思维】
- !hdu 4091--贪心、枚举--(思维)
- hdu 4803 Poor Warehouse Keeper (贪心思维)
- Hdu 5098 Smart Software Installer【思维+拓扑排序】
- HDU 6049 17多校2 Sdjpx Is Happy(思维题difficult)
- B - 逃生 HDU - 4857 中位数计数+思维
- hdu 3336 Count the string(思维可水过,KMP)
- HDU 6216 A Cubic number and A Cubic Number(思维)
- HDU 5969 最大的位或 (思维)
- HDU 5114 思维 + 数论
- 2015多校第7场 HDU 5372 Segment Game 树状数组,思维
- HDU - 6121 Build a tree(分治+思维)
- hdu 5873 思维题
- Hdu 6212 Zuma【思维+区间Dp】
- HDU-2050 - 折线分割平面 - 思维
- HDU 6045 Is Derek lying?(思维)
- 【hdu 6069】 Counting Divisors 【思维+数论】
- hdu 思维风暴