2016多校总结---HDU5812distance

本文介绍了一种关于素数运算的优化算法,该算法通过枚举最大公约数并维护一个特殊数组来加速查询过程,同时利用低比特位优化进一步提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


这个题目很巧妙。其实刚开始是没有任何想法的,但是仔细分析一下套路就会发现。其实是正常的套路。

首相,这是个询问类型的题。给你定义了一个函数d( x , y ) 为从x 到 y要乘或除的素数的最小个数。但是不可以0(n^2)肯定会超时(普遍的套路) ; 

但是这个题目要抓住一个特殊的点。因为这个是集合操作,包括了insert和delete, 可以在insert和delete的同时维护什么东西。这样的话query的时候可以直接使用这里面维护的东西。问题是维护什么

分析原式。可以知道d(x ,  y)  = f(x / gcd) + f(y / gcd) ; 这样就自然想到枚举gcd , x约数。那么知道了约数g,前半部分也就知道了,这样就只有一个未知数。f(y / g) ; 但是y必须在S内。不可能枚举S内的东西。所以可以在insert的同时记录。自然想到、记录d[x] ,表示约数是x的时候,S内的最小值。更新只需要在insert时d[x] = min(d[x]  , f(data / x)) ;

但是如果删除了data,怎么办。这样的话记录第二小,第三小。。。。最后等于维护一个链表,但是肯定是不行的。所以必须要换一个方法。考虑到一个关键点。每个数的素因子的个数小于20 ; 所以把20作为第二维。c[g][s] 表示g的约束,S中g的倍数data的f (data / g) = k 的个数 ; 因为题目中需要判断s是否存在一个data,如果存在就可以直接使用s代替f(y / gcd) ; 不需要确切的值,可以避免搜索,所以可以减少复杂度。这样。只需要维护C数组就可以做出口来了。

可以使用一些列的优化。但是注意query的时候,for(z=0;z<20;z++) 在判断是约束才进行,。这样可以搞出来 ,否者可能会TLE。  加上lowbit优化。就可以不用循环z那个20,但是要开一个数组维护个个bit位,因为s很小。。。也无所谓,如果S很大,那么就必须要优化。

代码900ms:,写的比较挫:

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX         1000005
#define   MAXN        2000005
#define   maxnode     500010
#define   sigma_size  30
#define   lson        l,m,rt<<1
#define   rson        m+1,r,rt<<1|1
#define   lrt         rt<<1
#define   rrt         rt<<1|1
#define   mid         int m=(r+l)>>1
#define   LL          long long
#define   ull         unsigned long long
#define   mem0(x)     memset(x,0,sizeof(x))
#define   mem1(x)     memset(x,-1,sizeof(x))
#define   meminf(x)   memset(x,INF,sizeof(x))
#define   lowbit(x)   (x&-x)
#define   S(n)        scanf("%d",&n)
#define   P(n)        printf("%d ",n)
#define   PN(n)       printf("%d\n",n)
#define   FP(k)       freopen(k , "r" ,stdin)
#define   RPTI(s , n) for(int i=s;i<n;i++)
#define   RPTJ(s , n) for(int j=s;j<n;j++)
#define   RPTK(s , n) for(int k=s;k<n;k++)
#define   RPTL(s , n) for(int l=s;l<n;l++)
const LL     mod   = 1000000;
///const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const int    INFF  = 1e9;
const double pi    = 3.141592653589793;
const double inf   = 1e18;
const double eps   = 1e-10;
inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }
    while(isdigit(tmp=getchar()));
    return ret;
}
/*******************************************/
typedef long long ll ;
const int moddd = 1e9 + 7 ;
int nums[1000005] ;
int prime[1000005] ;
int cntpri = 0 ;
int bin[1100005]  ;
int bin2[1100005] ;
void init(){
    mem0(nums) ;
    mem0(bin) ;
    for(int i=2;i<=1000000;i++){
        if(nums[i] == 0){
            nums[i] = 1 ;
            prime[cntpri++] = i ;
        }
        for(int j=0;j<cntpri&&i*prime[j]<=1000000;j++){
            nums[i*prime[j]] = nums[i] + 1 ;
            if(!(i % prime[j])) break ;
        }
    }
    int ci = 0 ;
    for(int i=1;i<=1100000;i*=2 , ci ++){
        bin[i] = ci , bin2[ci] = i ;
    }
}
int c[1000005][25] ;
int d[1000005] ;
void update(int i , int j , int val){
    if(c[i][j] == 0 && val == -1) d[i] = d[i] - bin2[j] ;
    else if(c[i][j] == 1 && val == 1) d[i] = d[i] + bin2[j] ;
}
void add(int data , int val){
    int s = sqrt(data) ;
    for(int i=1;i<=s;i++)if(data % i == 0){
        if(i * i == data){
            c[i][nums[data/i]] += val ;
            update(i , nums[data/i] , val) ;
        }
        else {
            c[i][nums[data/i]] += val ;
            update(i , nums[data/i] , val) ;
            c[data/i][nums[i]] += val ;
            update(data/i , nums[i] , val) ;
        }
    }
}
int main(){
    init() ;
    int n ;
    int ca = 0 ;
    set <int> s ;
    while(scanf("%d" , &n)!=EOF){
        if(n == 0) break ;
        if(s.size() != 0){
            set <int>::iterator si ;
            for(si = s.begin() ; si != s.end() ; si ++){
                int data = * si ;
                add(data , -1) ;
            }
            s.clear() ;
        }
        mem0(d) ;
        printf("Case #%d:\n" , ++ca) ;
        char str[10] ;
        int data ;
        for(int i=0;i<n;i++){
            scanf("%s"  , str) ;
            if(str[0] == 'I'){
                scanf("%d" , &data) ;
                if(s.count(data) == 1) continue ;
                else {
                    s.insert(data) ;
                    add(data , 1) ;
                }
            }
            else if(str[0] == 'D'){
                scanf("%d" ,&data) ;
                if(s.count(data) == 0) continue ;
                else {
                    s.erase(data) ;
                    add(data , -1) ;
                }
            }
            else if(str[0] == 'Q'){
                scanf("%d" , &data) ;
                int st = sqrt(data) ;
                int ans = INF ;
                if(s.size() == 0) {
                    printf("-1\n") ;
                    continue ;
                }
                for(int i=1;i<=st; i++){
                    if(data % i == 0) {
                        int td = 0 ;
                        if(d[i]){
                            td = bin[lowbit(d[i])] ;
                            ans = min(ans , td + nums[data/i]) ;
                        }
                        if(d[data/i]){
                            td = bin[lowbit(d[data/i])] ;
                            ans = min(ans , td + nums[i]) ;
                        }
                    }
                }
                printf("%d\n" , ans) ;
            }
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值