zoukankan      html  css  js  c++  java
  • @bzoj


    @description@

    若一个大于 1 的整数 M 的质因数分解有 k 项,其最大的质因子为 (A_k),并且满足 (A_k^k le N)(A_k < 128),我们就称整数 M 为 N - 伪光滑数。

    现在给出 N,求所有整数中第 K 大的 N - 伪光滑数。

    input
    只有一行,为用空格隔开的整数 N 和 K 。2 ≤ N ≤ 10^18, 1 ≤ K ≤ 800000,保证至少有 K 个满足要求的数
    output
    只有一行,为一个整数,表示答案。

    sample input
    12345 20
    sample output
    9167

    @solution@

    首先,如果对于质数 x 以及一个整数 k,满足 (x^k le N)。则只要保证剩下的 k-1 个质因数小于等于 x 就可以构成伪光滑数了。
    所以假如我们确定了 x 和 k,题目中给出的关于伪光滑数的约束统统没有用。

    @version - 1@

    关于求解第 k 大,这里显然二分答案是不可做的upd in 2019/2/12:还真的被我找到了二分答案的做法……不过我并不打算写因为我太懒了)。我们可以通过类比 k 短路的做法来做。简略地说一下:

    当给定 x 和 k 时,显然最大的数是 k 个 x 相乘,我们把这个当作初始状态。考虑设计一个不重复不遗漏的初始状态与某一状态之间的转移,且要使得转移始终是递减的。

    假如某状态 (m = p_1^{a_1}*p_2^{a_2}*dots*p_r^{a_r}),其中 (p_1<p_2<dots<p_r=x)。可以把这个状态看作 ([1, a_1+a_2+dots+a_r]) 这些位置填 (p_r),再 ([1, a_1+a_2+dots+a_{r-1}]) 这些位置填 (p_{r-1}) 并覆盖掉之前填的质因子,然后重复操作……

    于是,我们这样来转移:状态里面存储当前的数是什么,最小质因子和次小质因子是什么,最小质因子和次小质因子的出现次数。每次两类转移,次小换最小或者新增更小。前者要保证次小的质因子次数为正。

    upd in 2019/2/12:
    时间复杂度应该是 O(nlog^2n) 的级别。不过我们要相信玄学,一个数的质因子怎么可能卡得满这个上界。
    反正能过啦,管那么多干嘛。
    实际上,有一些代码实现可以做到 O(nlog n) 的复杂度,不过我看不懂……

    @version - 2@

    对于这道题,我们有一个更暴力(应该是吧?毕竟内存消耗更多些)的方法。
    我们一样给定 x 和 k。然后对于每一对 (x, k),我们把所有的可能的答案存储在堆内。

    当然不可能是直接存,我们使用可持久化左偏树来搞。

    定义 (f(x, k)) 表示最大的质因子为 x,质因子个数为 k 时的左偏树。
    讨论次大的数是什么,可以得到 (f(x, k)) 是由所有满足 i <= x 且 i 为质数的左偏树 (f(i, k-1)) 全部合并起来再给所有元素乘 x 得到。
    后一个操作可以用打 tag 的方式实现。

    但是这样空间开销还是很大。我们考虑给左偏树求前缀和。
    定义 (g(x, k)) 表示所有满足 i <= x 且 i 为质数的左偏树 (f(i, k)) 全部合并起来得到的左偏树。
    就有 (g(x, k)) 等于 (f(x, k))(g(x-1, k))的合并,(f(x, k)) 等于 (g(x, k-1)) 这棵树整体乘 x。
    这样每增加一个状态最多只会多合并一次。

    对,f 和 g 都是一个左偏树。
    对,你可以理解为左偏树用来作 dp。
    对,很神奇,我也没见过。

    下传标记的时候记得也要新建结点。

    @accepted code@

    因为我实在是太懒了,仅给出第二种方法的代码。
    upd in 2019/2/12:已经更新了第一种方法的代码啦~

    @version - 1@

    #include<queue>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXK = 800000;
    struct node{
    	int a1, a2, b1, b2; ll c;
    	node(int _a1=0, int _a2=0, int _b1=0, int _b2=0, ll _c=0):a1(_a1), a2(_a2), b1(_b1), b2(_b2), c(_c){}
    };
    bool operator < (node a, node b) {
    	return a.c < b.c;
    }
    bool is_prm(int n) {
    	for(int i=2;i<n;i++)
    		if( n % i == 0 ) return false;
    	return true;
    }
    int prm[128], pcnt;
    priority_queue<node>que;
    int main() {
    	ll N; int K;
    	scanf("%lld%d", &N, &K);
    	for(int i=2;i<128;i++) {
    		if( !is_prm(i) ) continue;
    		prm[++pcnt] = i;
    		for(ll nw=1,cnt=1;nw<=N/i;nw*=i,cnt++) 
    			que.push(node(0, pcnt, 0, cnt, nw*i));
    	}
    	ll ans;
    	for(int i=1;i<=K;i++) {
    		node t = que.top(); que.pop();
    		if( t.b1 > 1 )
    			que.push(node(t.a1, t.a2, t.b1 - 1, t.b2 + 1, t.c/prm[t.a1]*prm[t.a2]));
    		if( t.b2 > 1 ) {
    			for(int i=1;i<t.a2;i++)
    				que.push(node(t.a2, i, t.b2 - 1, 1, t.c/prm[t.a2]*prm[i]));
    		}
    		ans = t.c;
    	}
    	printf("%lld
    ", ans);
    }
    

    @version - 2@

    #include<queue>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXK = 800000;
    struct node{
    	node *ch[2];
    	ll key, tag; int dis;
    }pl[16000000 + 5], *rt1[128][64], *rt2[128][64], *tcnt, *NIL;
    void pushdown(node *x) {
    	if( x->tag != 1 ) {
    		if( x->ch[0] != NIL ) {
    			tcnt++; (*tcnt) = *(x->ch[0]);
    			tcnt->tag *= x->tag, tcnt->key *= x->tag;
    			x->ch[0] = tcnt;
    		}
    		if( x->ch[1] != NIL ) {
    			tcnt++; (*tcnt) = *(x->ch[1]);
    			tcnt->tag *= x->tag, tcnt->key *= x->tag;
    			x->ch[1] = tcnt;
    		}
    		x->tag = 1;
    	}
    }
    struct node2{
    	node *x;
    	node2(node *_x):x(_x){}
    };
    bool operator < (node2 a, node2 b) {
    	return a.x->key < b.x->key;
    }
    node *newnode(ll x) {
    	tcnt++;
    	tcnt->ch[0] = tcnt->ch[1] = NIL;
    	tcnt->key = x, tcnt->tag = 1, tcnt->dis = 1;
    	return tcnt;
    }
    node *merge(node *x, node *y) {
    	if( y == NIL ) return x;
    	if( x == NIL ) return y;
    	node *p = (++tcnt);
    	if( x->key < y->key ) swap(x, y);
    	(*p) = (*x); pushdown(p);
    	p->ch[1] = merge(p->ch[1], y);
    	if( p->ch[0]->dis < p->ch[1]->dis ) swap(p->ch[0], p->ch[1]);
    	p->dis = p->ch[1]->dis + 1;
    	return p;
    }
    void init() {
    	NIL = tcnt = &pl[0];
    	NIL->dis = 0;
    	for(int i=0;i<64;i++)
    		rt2[0][i] = NIL;
    	for(int i=0;i<128;i++)
    		rt2[i][0] = NIL;
    }
    bool is_prm(int n) {
    	for(int i=2;i<n;i++)
    		if( n % i == 0 ) return false;
    	return true;
    }
    int prm[128], mxk[128], pcnt;
    priority_queue<node2>que;
    int main() {
    	ll N; int K; init();
    	scanf("%lld%d", &N, &K);
    	for(int i=2;i<128;i++) {
    		if( !is_prm(i) ) continue;
    		prm[++pcnt] = i;
    		for(ll nw=1;nw<=N/i;nw*=i) mxk[pcnt]++;
    	}
    	for(int i=1;i<=pcnt;i++)
    		if( mxk[i] ) {
    			rt1[i][1] = newnode(prm[i]), rt2[i][1] = merge(rt2[i-1][1], rt1[i][1]);
    			for(int j=2;j<=mxk[i];j++) {
    				rt1[i][j] = (++tcnt); (*rt1[i][j]) = (*rt2[i][j-1]);
    				rt1[i][j]->key *= prm[i], rt1[i][j]->tag *= prm[i];
    				rt2[i][j] = merge(rt2[i-1][j], rt1[i][j]);
    			}
    	/*for(node *i=&pl[1];i<=tcnt;i++)
    		printf("%d %d %d %lld %lld
    ", i-pl, i->ch[0]-pl, i->ch[1]-pl, i->key, i->tag);*/
    		}
    	for(int i=1;i<=pcnt;i++)
    		for(int j=1;j<=mxk[i];j++)
    			que.push(rt1[i][j]);
    	ll ans;
    	for(int i=1;i<=K;i++) {
    		node2 f = que.top(); que.pop();
    		pushdown(f.x);
    		que.push(node2(merge(f.x->ch[0], f.x->ch[1])));
    		ans = f.x->key;
    	}
    	printf("%lld
    ", ans);
    }
    

    @details@

    一开始我在合并的时候,某一个结点等于 NIL 的时候新建了一个结点来存另一个结点。
    但是极限数据始终不是 RE 就是 MLE。
    后来我选择直接返回另一个结点,然后就没问题了。

    连这个空间也要卡吗……

  • 相关阅读:
    第三次作业
    第二次作业
    第一次作业
    第五次作业
    第四次作业
    第三次作业
    刘存俊第二次作业
    数据压缩第一次作业
    第五次作业
    第四次作业
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10287082.html
Copyright © 2011-2022 走看看