• [2020牛客暑期多校训练营(第一场)虚树 Infinite Tree]


    2020牛客暑期多校训练营(第一场)虚树 Infinite Tree

    题解参考博客:https://blog.nowcoder.net/n/df889adfaf824d50ad2291f4d2eb04a2?&toCommentId=6480068

    题目大意:

    定义 (mindiv(n))(n) 最小的大于1的约数,对于每一个 (i)(1<=i<=m) 建了一条边 ((i,frac{i}{mindiv[i]})) ,给你一个 (m) ,表示用m个关键点,每一个关键点的值是 (i!) ,让你用这m个关键点建一颗树,然后给你 m个 (wi) 让你求 (min_usum_{i=1}^{m}wi*c(u,i!)) ,其中 (c(u,i!)) 表示 (u) 这个点和 (i) 这个点之间的边的数量,这个 (u) 则是这棵树上的任意一个点。

    题解:

    这个题目前置技能点是虚树,如果不会,先学习虚树。

    学习博客:https://www.cnblogs.com/EchoZQN/p/13330893.html

    学会虚树了,然后开始分析这个题目。

    • 首先为什么要用虚树,这个是因为这棵树太大了,无法存下来。

    • 怎么建虚树?

      • 建一棵虚树需要什么:原树关键点的 (dfs) 序,原数关键点的 (LCA)
      • 这两个知道之后,就可以直接套模板了
    • 怎么求原树关键点的 (dfs) 序?

      • 首先我们比较 (i!)((i+1)!)

      • 我们定义 (dfs) 序:

        picture1

      • ((i+1)!) 相对于 (i!) ,多了一个约数 ((i+1)) ,如果这个 ((i+1)) 是一个质数,是不是 ((i+1)!)(dfs) 序一定在 (i!) 后面,如果这个不是一个质数,那么如果 (i+1) 存在约数大于 (i!) 的最小素因子,那么是不是 ((i+1)!) 会分向右分叉,如果没有,那就是都等于最小素因子,那么是不是会延长,所以 ((i+1)!)(dfs) 序一定大于 (i!)

      • 所以最终结果就是对于这 (m) 个关键点,他们的 (dfs) 序和本身大小成正比。

    • 接下来说说怎么求 (LCA)

      • 假设 (u=p_{1}^{a1}p_{2}^{a1}...p_{x}^{ax}) (v=p_{1}^{a1}p_{2}^{a2}...p_{y}^{ay}) (p按照顺序排序)

      • 那么,他们相同的节点就是从后往前比较第一个不同的位置,也就是第一个 ((p_i!=p_j||ai!=aj)) 这个位置就是他们的 (LCA)

      • 因为 (dfs) 序是和本身大小成正比的,所以对于数 (i!) 和 $(i+1)! $ ,假设 (lca1=LCA((i-1)!,i!))(lca2=LCA(i!,(i+1)!)) 只有三种关系:

        • (lca1==lca2) 直接建边
        • (lca2<lca1) 重新给这个 (lca) 一个编号,然后建边
        • (lca2=1) 直接建边
    • 最后总结一下怎么求 (LCA)

      • 首先用线段树维护一下这个位置和栈顶元素的 (LCA) 的深度
      • 这个维护参考上面。
      • 假设当前位置是 (p) ,栈顶元素是 (x) ,栈第二个元素是 (y)
      • 先求 (lca=LCA(p,x))
        • (dep[lca]==dep[y]) 说明 (lca=y)
        • (dep[lca]>dep[y]) 则说明 (lca) 还在 (y) 这个节点之上,丢掉栈顶元素,继续判断
        • (dep[lca]<dep[y]) 说明 (lca) 还没有入栈,那就给这个 (lca) 一个编号,然后让他入栈,建边
    #include <bits/stdc++.h>
    #define fir first
    #define sec second
    #define inf 0x3f3f3f3f
    #define inf64 0x3f3f3f3f3f3f3f3f
    #define debug(x) printf("debug:%s=%d
    ",#x,x);
    //#define debug(x) cout << #x << ": " << x << endl
    using namespace std;
    const int maxn = 2e5+10;
    typedef long long ll;
    int head[maxn<<1],nxt[maxn<<1],to[maxn<<1],cnt;
    void ADD(int u,int v){
    //    printf("u=%d v=%d
    ",u,v);
        ++cnt,to[cnt]=v,nxt[cnt]=head[u],head[u]=cnt;
        ++cnt,to[cnt]=u,nxt[cnt]=head[v],head[v]=cnt;
    }
    
    int sum[maxn*4];//Ï߶ÎÊ÷ά»¤lca
    void build(int id,int l,int r){
        sum[id]=0;
        if(l==r) return ;
        int mid=(l+r)>>1;
        build(id<<1,l,mid);
        build(id<<1|1,mid+1,r);
    }
    void update(int id,int l,int r,int pos,int val){
        if(l==r){
            sum[id]+=val;
            return ;
        }
        int mid=(l+r)>>1;
        if(pos<=mid) update(id<<1,l,mid,pos,val);
        else update(id<<1|1,mid+1,r,pos,val);
        sum[id]=sum[id<<1]+sum[id<<1|1];
    }
    
    int query(int id,int l,int r,int x,int y){
        if(x<=l&&y>=r) return sum[id];
        int mid=(l+r)>>1,ans=0;
        if(x<=mid) ans+=query(id<<1,l,mid,x,y);
        if(y>mid) ans+=query(id<<1|1,mid+1,r,x,y);
        return ans;
    }
    
    int dep[maxn],w[maxn],stk[maxn],cur,now,n;
    int isp[maxn],tot,v[maxn];
    void init() {
        tot = 0;
        memset(v,0,sizeof(v));
        for (int i = 2; i < maxn; ++i) {
            if (!v[i]) {
                v[i] = i;
                isp[++tot] = i;
            }
            for (int j = 0; j < tot; ++j) {
                if (1ll * i * isp[j] >= maxn) break;
                v[i * isp[j]] = isp[j];
            }
        }
    }
    int num;
    pair<int,int>prime[maxn];
    void judge(int x) {
        int y = x;
        num = 0, dep[y] = 0;
        int f = lower_bound(isp + 1, isp + 1 + tot, 500) - isp;
        for (int i = 1; i <= f; i++) {
            int cur = 0;
            while (x % isp[i] == 0) x /= isp[i], cur++;
            if (cur) prime[++num] = make_pair(i, cur);
            dep[y] += cur;
            if (x == 1) break;
        }
        if (x != 1) {
            dep[y]++;
            int pos = lower_bound(isp + 1, isp + 1 + tot, x) - isp;
            prime[++num] = make_pair(pos, 1);
        }
    //    debug(y)
    //    debug(dep[y])
    }
    
    void modify(){
    	for(int i=1;i<=num;i++) update(1,1,n,prime[i].fir,prime[i].sec);
    }
    
    void insert(int p) {
    //	printf("p=%d
    ",p);
        judge(p);
        dep[p]+=dep[p-1];
    //    printf("dep[%d]=%d
    ",p,dep[p]);
        int x = stk[cur];
        if (x == 1) {
        	modify();
            stk[++cur] = p;
            return;
        }
        int pos = prime[num].fir;
        int d = query(1, 1, n, pos, n);
    //    debug(d)
    //    debug(dep[stk[cur]])
        while (d != dep[stk[cur]]) {
            int y = stk[--cur];
    //        debug(y)
            if(dep[y]<d){
            	++now,dep[now]=d;
    			ADD(now,x),stk[++cur]=now;
                break;
            }
            if(dep[y] == d){
            	ADD(y,x);
            	break;
    		}
            ADD(x,y),x=stk[cur];
        }
    //    debug(cur)
    //    debug(stk[cur])
        stk[++cur]=p;
        modify();
    }
    
    int m;
    ll dp[maxn],have[maxn],ans;
    
    void dfs1(int u,int pre){
    //	printf("u=%d
    ",u);
        dp[u]=0,have[u]=0;
        if(u<=m&&u>=1) have[u]=w[u];
        for(int i=head[u];i;i=nxt[i]){
            int v = to[i];
            if(v==pre) continue;
            dfs1(v,u);
            have[u]+=have[v];
            ll dis = dep[v]-dep[u];
    //        printf("u=%d v=%d dis=%lld
    ",u,v,dis);
            dp[u]+=dp[v]+have[v]*dis;
        }
    //    printf("dp[%d]=%lld
    ",u,dp[u]);
    }
    
    void dfs2(int u,int pre){
    //	debug(u);
    	ans=min(ans,dp[u]);
        for(int i=head[u];i;i=nxt[i]){
            int v = to[i];
            if(v==pre) continue;
            ll dis = dep[v]-dep[u];
    //        printf("dp[%d]=%lld dp[%d]=%lld have[%d]=%d have[%d]=%d dis=%lld
    ",u,dp[u],v,dp[v],u,have[u],v,have[v],dis);
            dp[v]=dp[u]-have[v]*dis+(have[u]-have[v])*dis;
    //        printf("dp[%d]=%lld
    ",v,dp[v]);
            have[v]=have[u];
            dfs2(v,u);
        }
    	have[u]=0,head[u]=0;
    }
    
    void solve(){
        now=m,cnt=0,stk[cur=1]=1;
        for(int i=2;i<=m;i++) insert(i);
        while(--cur) ADD(stk[cur],stk[cur+1]);
    	ans = inf64;
        dfs1(1,0),dfs2(1,0);
        printf("%lld
    ",ans);
        return ;
    }
    
    int main(){
        init();
        while(scanf("%d",&m)!=EOF){
            n = lower_bound(isp+1,isp+1+tot,m)-isp;
            for(int i=1;i<=m;i++) scanf("%d",&w[i]);
            build(1,1,n);solve();
        }
    }
    
    
    
  • 相关阅读:
    MIME类型大全
    Asp.net中解决“请求超时”的问题
    C#日期函数所有样式大全
    [转]Oracle数据关联查询
    convert时间格式转换参数表 [收藏]
    vs2008安装失败。解决办法。部分。
    asp.net获取web.config配置信息
    jQuery UI Dialog控件中的表单无法正常提交的解决方法
    控制Button在数据验证成功才执行后台方法
    关于使用DataTable.Compute()方法时报“聚合参数中的语法错误: 需要具有可能的“Child”限定符的单个列参数。”
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/13333763.html
Copyright © 2020-2023  润新知