zoukankan      html  css  js  c++  java
  • [CF1292D] Chaotic V.

    官方题解
    在我的blog阅读

    (K = max{k_i})
    貌似比原题解的复杂度正确一点,(O(Klog^2 K ))

    题意

    给一棵树,(i)(dfrac{i}{minFactor(i)})连边

    (n)个关键点,每个点的位置是(k_i!)

    求一个点到所有关键点距离最短(重复算多次),(n leq 10^6, k_i leq 5 imes10^3)

    思路

    • 到所有点的最短距离其实等价于求树的重心(点、边均带权)
    • 构出虚树就可以解决

    首先发现,LCA的深度就是公共最大质因子个数,每个点的分叉的位置就是第一次质因子改变的地方。如(5^2)变成(5^3)就会分叉。

    依次插入(1!)(5000!),设插入(i!)

    显然,插入一个质数一定会连接(1),否则对(i)做质因子分解,第一次改变的质因子一定是(i)的最大质因子

    所以可以求出这个质因子,((i-1)!)中大于等于它的质因子不会分叉,然后在这个位置分叉(可能在一条虚边上,需要拆开边,开一个新点)。

    用树状数组维护((i-1)!)的各个质因子出现个数,就可以快速求出公共部分的长度,记为(com),然后将((i-1)!)的质因子个数和记为(facSum)(facSum - com)就是要跳的步数,暴力向上跳,然后需要开新点就开新点,不需要就直接挂下面就好了,连一条长为(facSum' - com)(facSum')(i!)的质因子个数)即可,这里有点类似SAM插入。

    最后从根节点开始贪心地进入(size geq lfloor dfrac{n}{2} floor)的子树即可。

    复杂度

    • (k)次查询树状数组,(O(K log K))
    • 每次分解质因子插入树状数组,(O(K log ^2 K))(最多有(O(K log K))个质因子)
    • 每次暴力向上跳,因为每次深度(+1)(指虚树),所以(O(K))
    • 上界不是很紧

    Code

    CF提交记录
    好像写长了点...

    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <cassert>
    using namespace std;
    #define File(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)
    typedef long long ll;
    namespace io {
    	const int SIZE = (1 << 21) + 1;
    	char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
    	#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
    	char getc () {return gc();}
    	inline void flush () {fwrite (obuf, 1, oS - obuf, stdout); oS = obuf;}
    	inline void putc (char x) {*oS ++ = x; if (oS == oT) flush ();}
    	template <class I> inline void gi (I &x) {for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); x *= f;}
    	template <class I> inline void print (I x) {if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;while (x) qu[++ qr] = x % 10 + '0',  x /= 10;while (qr) putc (qu[qr --]);}
    	struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
    }
    using io :: gi; using io :: putc; using io :: print; using io :: getc;
    template<class T> void upmax(T &x, T y){x = x>y ? x : y;}
    template<class T> void upmin(T &x, T y){x = x<y ? x : y;}
    const int N = 5005, V = 5000;
    bool np[N];
    int p[N], pid[N], minFac[N], pc = 0;
    
    void pre(){
    	np[0] = np[1] = true;
    	for(int i=2; i<=V; i++){
    		if(!np[i]) p[++pc] = i, pid[i] = pc;
    		for(int j=1; j<=pc && i * p[j] <= V; j++){
    			np[i * p[j]] = true;
    			minFac[i * p[j]] = p[j];
    			if(i % p[j] == 0) break;
    		}
    	}
    }
    
    int fa[N * 2], len[N * 2], cnt[N];
    int tr[N], sz[N*2];
    void modify(int p){
    	// printf("Modify %d
    ", p);
    	p = pc - p + 1;
    	// assert(p > 0 && p <= pc);
    	for(; p<=pc; p += p & -p) ++tr[p];
    }
    int query(int p){
    	p = pc - p + 1;
    	int res = 0;
    	for(; p; p ^= (p & -p)) res += tr[p];
    	return res;
    }
    
    struct Edge{
    	int v, w;
    	Edge(){}
    	Edge(int _v, int _w) : v(_v), w(_w) {}
    };
    vector<Edge> G[N*2];
    
    int nc = 1;
    ll res = 0;
    int Node[N], Sum[N];
    
    int getDepth(int x){
    	int res = 0;
    	while(x != 1) res += len[x], x = fa[x];
    	return res;
    }
    
    void build(){
    	nc = 1; sz[nc] = cnt[1] + cnt[0];
    	int facSum = 0;
    	for(int i=2; i<=V; i++){
    		if(!np[i]){
    			int now = ++nc;
    			fa[now] = 1; len[now] = ++facSum;
    			sz[now] = cnt[i];
    			modify(pid[i]);
    			res += 1LL * facSum * cnt[i];
    			continue;
    		}
    		int v = i, facCnt = 0;
    		while(np[v]) v /= minFac[v], ++facCnt;
    		++facCnt;
    		int com = query(pid[v]), diff = facSum - com;
    		int x = nc;
    		while(diff >= len[x])
    			diff -= len[x], x = fa[x];
    		if(diff == 0){
    			int now = ++nc;
    			fa[now] = x;
    			facSum += facCnt; len[now] = facSum - com;
    			sz[now] = cnt[i];
    		}
    		else{
    			int p = ++nc;
    			fa[p] = fa[x]; len[p] = len[x] - diff;
    			fa[x] = p; len[x] = diff;
    			int now = ++nc;
    			fa[now] = p;
    			facSum += facCnt; len[now] = facSum - com;
    			sz[now] = cnt[i];
    		}
    		v = i;
    		while(np[v]) modify(pid[minFac[v]]), v /= minFac[v];
    		modify(pid[v]);
    		res += 1LL * facSum * cnt[i];
    	}
    }
    
    void dfs(int x, int fa){
    	for(const Edge &e : G[x]){
    		if(e.v == fa) continue;
    		dfs(e.v, x);
    		sz[x] += sz[e.v];
    	}
    }
    
    int main(){
    	// File("cf1292d");
    	int n;
    	gi(n);
    	for(int i=1, x; i<=n; i++) gi(x), ++cnt[x];
    	pre();
    	build();
    	for(int i=nc; i>=2; i--) G[fa[i]].emplace_back(i, len[i]);
    	dfs(1, 1);
    	int p = 1;
    	bool flag = true;
    	while(flag){
    		flag = false;
    		for(const Edge &e : G[p]){
    			if(sz[e.v] > (n >> 1)){
    				res -= 1LL * e.w * ((sz[e.v] << 1) - n);
    				p = e.v;
    				flag = true;
    				break;
    			}
    		}
    	}
    	printf("%lld
    ", res);
    	return 0;
    }
    
  • 相关阅读:
    好用,Office超效率速成技
    Oracle 12c从入门到精通:视频教学超值版
    用Excel学数据分析
    VMware、Citrix和Microsoft虚拟化技术详解与应用实践
    中文版Dreamweaver CC+Flash CC+Photoshop CC网页设计基础培训教程(新编实战型全功能培训教材)
    1467.二叉排序树
    1177.查找
    1178.复数集合
    1165.字符串匹配
    1166.迭代求立方根
  • 原文地址:https://www.cnblogs.com/RiverHamster/p/sol-cf1292d.html
Copyright © 2011-2022 走看看