P2495 [SDOI2011]消耗战

本文深入探讨了虚树上的动态规划(DP)算法,详细解释了如何通过构造虚树来解决特定类型的路径问题,特别是在涉及大量查询的关键点信息场景下。文章提供了具体的实现步骤和代码示例,包括虚树构建过程、LCA查询、以及DP状态转移方程。

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

思路

虚树上DP

虚树相当于一颗包含了所有询问的关键点信息的树,包含的所有点只有询问点和它们的LCA,所以点数是\(2k\)级别的,这样的话复杂度就是\(O(\sum k)\),复杂度就对了
虚树重点就是虚树的构造
用栈可以进行虚树的构造
过程如下

设现在加入点u
如果栈为空或只有一个元素,直接加入即可(延长当前链)
如果LCA(u,S[top])=S[top],把u加入即可(延长树链)
否则证明u和S中的树链在lca的两个子树中,在dfn[lca]<=dfn[S[top-1]]的条件下,从S[top-1]向S[top]连边,然后弹出
如果最后lca=S[top],证明这颗子树构造完成,加入u即可
否则证明lca在S[top-1]和S[top]之间,从lca向S[top]连边,然后pop出S[top],lca入栈
最后把u加入即可

这题建出虚树之后就直接DP就好了

如果u不是关键点
\(DP[u]=\sum_{v\in son[u]} min(minx[v],DP[v])\)
如果u是关键点
\(DP[u]=minx[u]\)

minx[u]是断开1到u路径的最小代价

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <vector>
#define int long long
using namespace std;
const int MAXN = 250010;
int n,m;
struct Graph{
    vector<int> to[MAXN],wx[MAXN];
    void addedge(int ui,int vi,int wi){
        to[ui].push_back(vi);
        wx[ui].push_back(wi);
    }
}G1,G2;
int S[MAXN],topx,dfn[MAXN],dfs_clock,fa[MAXN][20],dep[MAXN],minx[MAXN],mark[MAXN];
void dfs1(int u,int f){
    dep[u]=dep[f]+1;
    dfn[u]=++dfs_clock;
    fa[u][0]=f;
    for(int i=1;i<20;i++)
        fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int i=0;i<G1.to[u].size();i++){
        int vi=G1.to[u][i];
        if(vi==f)
            continue;
        minx[vi]=min(G1.wx[u][i],minx[u]);
        dfs1(vi,u);
    }
}
int lca(int x,int y){
    if(dep[x]<dep[y])
        swap(x,y);
    for(int i=19;i>=0;i--)
        if(dep[fa[x][i]]>=dep[y])
            x=fa[x][i];
    if(x==y)
        return x;
    for(int i=19;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
bool cmp(int a,int b){
    return dfn[a]<dfn[b];
}
void insert(int u){
    if(topx<=1){
        S[++topx]=u;
        return;
    }
    int Lca=lca(u,S[topx]);
    if(Lca==S[topx]){
        S[++topx]=u;
        return;
    }
    while(topx>1&&dfn[Lca]<=dfn[S[topx-1]]){
        G2.addedge(S[topx-1],S[topx],0);
        topx--;
    }
    if(Lca!=S[topx]){
        G2.addedge(Lca,S[topx],0);
        S[topx]=Lca;
    }
    S[++topx]=u;
}
int dfs2(int u){
    int ans=0;
    for(int i=0;i<G2.to[u].size();i++)
        ans+=min(minx[G2.to[u][i]],dfs2(G2.to[u][i]));        
    G2.to[u].clear();
    if(mark[u]){
        mark[u]=false;
        return minx[u];
    }
    else
        return ans;
}
vector<int> im;
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<n;i++){
        int a,b,c;
        scanf("%lld %lld %lld",&a,&b,&c);
        G1.addedge(a,b,c);
        G1.addedge(b,a,c);
    }
    minx[1]=0x3f3f3f3f;
    dfs1(1,0);
    scanf("%lld",&m);
    for(int i=1;i<=m;i++){
        im.clear();
        int x,k;
        scanf("%lld",&k);
        for(int j=1;j<=k;j++){
            scanf("%lld",&x);
            im.push_back(x);
            mark[x]=true;
        }
        sort(im.begin(),im.end(),cmp);
        insert(1);
        for(int i=0;i<im.size();i++)
            insert(im[i]);
        while(topx>0){
            G2.addedge(S[topx-1],S[topx],0);
            topx--;
        }
        printf("%lld\n",dfs2(1));
    }
    return 0;
}

转载于:https://www.cnblogs.com/dreagonm/p/10748892.html

P2152 [SDOI2009] SuperGCD是一道经典的算法题,目标是计算两个大整数的最大公约数 (GCD),并输出结果。由于题目中的数字规模非常庞大(每个数字可能有超过1万位),普通的欧几里得算法无法直接使用内置数据类型完成运算,因此需要借助高精度算法。 ### 解答思路 #### 1. **欧几里得算法** 欧几里得算法的核心思想基于公式 `gcd(a, b) = gcd(b, a % b)`,其中 `%` 表示取模操作。当其中一个数值降为零时,另一个值即为最大公约数。 #### 2. **高精度实现** 题目给定的数据范围远超常规64位整型所能表示的范围,所以需要用字符串或其他自定义的方式来存储和处理大整数。 ##### 主要步骤包括: - 将输入的大整数作为字符串读入,并将其转化为可以逐位参与运算的形式; - 编写一个函数用于求解两非负整数间除法余数的功能; - 实现类似欧几里得辗转相除的方式递归或迭代地寻找最大公约数; #### 3. **优化点** 为了提高效率,在实际编写代码的时候可能会加入一些小技巧来减少不必要的计算量: - 如果两者均为偶数,则先提取出所有的公因子“2”再继续后续更复杂的GCD过程; - 直接利用二进制形式来进行快速比较大小以及移位替代乘除等操作以加速程序运行速度; 下面提供一段伪码描述整体流程: ``` function super_gcd(A,B): if B == "0": return A else: remainder <- calc_modulo(A,B) return super_gcd(B,remainder) function main(): read inputs as strings A and B result <- super_gcd(max(A,B), min(A,B)) print(result) function calc_modulo(Dividend,Divisor): // Implement division using subtraction for large numbers represented by string. // Return the remaining part after complete divisions. ``` 以上就是对于该问题的基本解决策略概述了!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值