LuoGuP2607:[ZJOI2008]骑士

本文介绍了一种基于基环树的动态规划方法,通过断开环并将断点作为根节点进行DP运算来解决特定问题。文章提供了两种实现代码,一种是非标准实现,另一种是标准实现,并对代码进行了详细解释。
摘要由CSDN通过智能技术生成

Pre

之前做过这一题,方法比较麻烦。

Solution

基环树乱搞。

简便的是把环断开,以两个断点为根进行 \(DP\) 这样就可以算出答案。

注意到 \(STD\) 代码里面是有一句

dp[v][1] = -1e9;

这样就强制的选了一个,不选另外一个。

Code

Code(!std)

#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<unistd.h>
#include<utility>
#include<map>
#include<limits.h>
#include<cmath>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define rint register int
#define ll long long
#define I inline
#define ill I ll
#define iint I int
#define ivoid I void
#define ifloat I float
#define idouble I double
#define ibool bool
#define ipair I pair
#define xx first
#define yy second
using namespace std;
int mod;
ill qpow(ll m,ll n)
{
    ll base=m,tot=1;
    while(n)
    {
        if(n%2==1){
            tot*=base;
            tot%=mod;
        }
        base*=base;
        base%=mod;
        n/=2;
    }
    return tot%mod;
}
struct _in{
    const _in&operator,(rint&a)const{
        a=0;
        rint f=1;
        char k=getchar();
        while(k>'9'||k<'0'){if(k=='-')f=-1;k=getchar();}
        while(k>='0'&&k<='9'){a=a*10+k-'0';k=getchar();}
        a*=f;
        return*this;
    }
}in;

const int maxn=1005000;

//链式前向星
int h[maxn],fr[maxn],to[maxn],tot;
ivoid add(rint,rint);
//End

int n;
int fa[maxn],dep[maxn];
ll data[maxn];
int sign;
ll ans=0;
bool ed[maxn];
ll f[maxn][3];

void dp(rint,rint);

int main()
{
    //freopen("data.in","r",stdin);
    in,n;
    for(rint i=1;i<=n;i++){
        scanf("%lld",&data[i]);
        in,fa[i];
        add(fa[i],i);
    }
    for(rint i=1;i<=n;i++){
        if(!ed[i]){
            rint root=i;
            ed[root]=1;
            while(!ed[fa[root]]){
                root=fa[root];
                ed[root]=1;
            }
            dp(root,root);
            ll tmax=f[root][0];
            // for(rint i=1;i<=n;i++) printf("%d %d\n",f[i][0],f[i][1]);
            if(root!=fa[sign]){
               pair<ll,ll>now,pre;
               now=make_pair(f[sign][0],f[sign][0]);
               pre=make_pair(f[sign][0],f[sign][1]);
               rint Now=fa[sign];
               while(1)
               {
                    pair<ll,ll> tmp=make_pair(f[Now][0],f[Now][1]);
                    f[Now][0]-=max(pre.xx,pre.yy);
                    f[Now][0]+=max(now.xx,now.yy);
                    f[Now][1]-=pre.xx;
                    f[Now][1]+=now.xx;
                    pre=tmp;
                    now=make_pair(f[Now][0],f[Now][1]);
                    if(Now==root) break;
                    Now=fa[Now];
               }
            }
            ans+=max(f[root][1],max(tmax,f[root][0]));
        }
    }
    printf("%lld\n",ans);
    return 0;
}


void dp(rint pos,rint root)
{
    f[pos][1]=data[pos];
    dep[pos]=dep[fa[pos]]+1;
    ed[pos]=1;
    for(rint i=h[pos];i;i=fr[i]){
        if(to[i]!=root){
            dp(to[i],root);
            f[pos][1]+=f[to[i]][0];
            f[pos][0]+=max(f[to[i]][0],f[to[i]][1]);
        }
        else{
            sign=pos;
        }
    }
}

ivoid add(rint u,rint v)
{
    tot++;
    fr[tot]=h[u];
    to[tot]=v;
    h[u]=tot;
}

Code(std)

#include <bits/stdc++.h>
#define LL long long
#define maxn 1000010
using namespace std;
struct Edge{
    int to, next;
}edge[maxn << 1];
int num, head[maxn], a[maxn], f[maxn], vis[maxn], n;
LL dp[maxn][2], ans;

inline int read(){
    int s=  0,w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

void addedge(int x, int y){ edge[++num] = (Edge){y, head[x]}, head[x] = num; }

void dfs(int u, int x){
    vis[u] = 1;
    dp[u][0] = 0, dp[u][1] = a[u];
    for (int i = head[u]; i; i = edge[i].next){
        int v = edge[i].to;
        if (v != x){
            dfs(v, x);
            dp[u][0] += max(dp[v][0], dp[v][1]);
            dp[u][1] += dp[v][0];
        } else dp[v][1] = -1e9;
    }
}

void solve(int u){ 
    while (!vis[u]) vis[u] = 1, u = f[u];
    dfs(u, u);
    LL sum = max(dp[u][0], dp[u][1]);
    u = f[u];
    dfs(u, u);
    ans += max(sum, max(dp[u][0], dp[u][1]));
}

int main(){
    n = read();
    for (int i = 1; i <= n; ++i){
        a[i] = read(), f[i] = read();
        addedge(f[i], i);
    }
    for (int i = 1; i <= n; ++i)
        if (!vis[i]) solve(i);
    printf("%lld\n", ans);
    return 0;
}

Conclusion

有时候考虑一些简单的做法会有很大的好处。

转载于:https://www.cnblogs.com/ChiTongZ/p/11368705.html

内容概要:该题库专为研究生入学考试计算机组成原理科目设计,涵盖名校考研真题、经典教材课后习题、章节题库和模拟试题四大核心模块。名校考研真题精选多所知名高校的计算机组成原理科目及计算机联考真题,并提供详尽解析,帮助考生把握考研命题趋势与难度。经典教材课后习题包括白中英《计算机组成原理》(第5版)和唐朔飞《计算机组成原理》(第2版)的全部课后习题解答,这两部教材被众多名校列为考研指定参考书目。章节题库精选代表性考题,注重基础知识与重难点内容,帮助考生全面掌握考试大纲要求的知识点。模拟试题依据历年考研真题命题规律和热门考点,精心编制两套全真模拟试题,并附标准答案,帮助考生检验学习成果,评估应试能力。 适用人群:计划参加研究生入学考试并报考计算机组成原理科目的考生,尤其是需要系统复习和强化训练的学生。 使用场景及目标:①通过研读名校考研真题,考生可以准确把握考研命题趋势与难度,有效评估复习成效;②通过经典教材课后习题的练习,考生可以巩固基础知识,掌握解题技巧;③通过章节题库的系统练习,考生可以全面掌握考试大纲要求的各个知识点,为备考打下坚实基础;④通过模拟试题的测试,考生可以检验学习成果,评估应试能力,为正式考试做好充分准备。 其他说明:该题库不仅提供详细的题目解析,还涵盖了计算机组成原理的各个方面,包括计算机系统概述、数据表示与运算、存储器分层、指令系统、中央处理器、总线系统和输入输出系统等。考生在使用过程中应结合理论学习与实践操作,注重理解与应用,以提高应试能力和专业知识水平。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值