BZOJ 1040 骑士(环套树DP)

如果m=n-1,显然这就是一个经典的树形dp。

现在是m=n,这是一个环套树森林,破掉这个环后,就成了一个树,那么这条破开的边连接的两个顶点不能同时选择。我们可以对这两个点进行两次树形DP根不选的情况。

那么答案就是每个森林的max()之和。

# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <set>
# include <cmath>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi acos(-1.0)
# define eps 1e-3
# define MOD 1000000007
# define INF (LL)1<<60
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FO(i,a,n) for(int i=a; i<n; ++i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
int Scan() {
    int res=0, flag=0;
    char ch;
    if((ch=getchar())==‘-‘) flag=1;
    else if(ch>=‘0‘&&ch<=‘9‘) res=ch-‘0‘;
    while((ch=getchar())>=‘0‘&&ch<=‘9‘)  res=res*10+(ch-‘0‘);
    return flag?-res:res;
}
void Out(int a) {
    if(a<0) {putchar(‘-‘); a=-a;}
    if(a>=10) Out(a/10);
    putchar(a%10+‘0‘);
}
const int N=1000005;
//Code begin...

struct Edge{int p, next, flag;}edge[N<<1];
int head[N], cnt=2, node[N], res[2], p;
bool vis[N];
LL dp[2][N][2];

void add_edge(int u, int v){edge[cnt].p=v; edge[cnt].next=head[u]; head[u]=cnt++;}

void dfs(int x, int fa)
{
    vis[x]=true;
    for (int i=head[x]; i; i=edge[i].next) {
        int v=edge[i].p;
        if (v==fa) continue;
        if (vis[v]==true) {
            if (p==0) res[0]=x, res[1]=v, p=1, edge[i].flag=edge[i^1].flag=1;
            continue;
        }
        dfs(v,x);
    }
}
void dfs_dp(int x, int fa, int flag)
{
    for (int i=head[x]; i; i=edge[i].next) {
        int v=edge[i].p;
        if (v==fa||edge[i].flag) continue;
        dfs_dp(v,x,flag);
        dp[flag][x][0]+=max(dp[flag][v][0],dp[flag][v][1]);
        dp[flag][x][1]+=dp[flag][v][0];
    }
    dp[flag][x][1]+=node[x];
}
int main ()
{
    int n, u, w;
    LL ans=0;
    scanf("%d",&n);
    FOR(i,1,n) scanf("%d%d",&w,&u), add_edge(u,i), add_edge(i,u), node[i]=w;
    FOR(i,1,n) {
        if (vis[i]) continue;
        p=0; dfs(i,0);
        dfs_dp(res[0],0,0); dfs_dp(res[1],0,1);
        ans+=max(dp[0][res[0]][0],dp[1][res[1]][0]);
    }
    printf("%lld\n",ans);
    return 0;
}

时间: 2024-10-26 06:26:50

BZOJ 1040 骑士(环套树DP)的相关文章

【BZOJ】1040: [ZJOI2008]骑士 环套树DP

[题意]给定n个人的ai和bi,表示第i个人能力值为ai且不能和bi同时选择,求能力值和最大的选择方案.n<=10^6. [算法]环套树DP(基环树) [题解]n个点n条边--基环森林(若干环套树子图). 若原图是树,经典DP做法:f[i][0/1]表示i点选或不选的最大能力值和,则f[i][0]=Σmax{f[j][0],f[j][1]},f[i][1]=Σf[j][0]+a[i],j=son[i]. 找环:dfs到访问过的点,标记环上的一条边. 破环:和普通树上DP唯一的区别是,标记边两端不

[BZOJ1040][ZJOI2008]骑士(环套树dp)

1040: [ZJOI2008]骑士 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 5816  Solved: 2263[Submit][Status][Discuss] Description Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各 界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战火绵延五百里,在和平环境 中安逸了数百年的Z国又怎能抵挡的住Y国的军队.于是人

BZOJ 1791 岛屿(环套树+单调队列DP)

题目实际上是求环套树森林中每个环套树的直径. 对于环套树的直径,可以先找到这个环套树上面的环.然后把环上的每一点都到达的外向树上的最远距离作为这个点的权值. 那么直径一定就是从环上的某个点开始,某个点结束的. 把环拆成链,定义dp[i]表示第i个点为结束点的最远距离,显然有dp[i]=val[j]+sum[i]-sum[j-1]+val[i].显然可以用单调队列优化这个DP. 剩下的就是这样依次统计每个环套树的直径之和. 对于环套树上找环可以借鉴最小树形图找环的技巧. 首先将边定向,保证每个点的

BZOJ1040:骑士(基环树DP)

Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战火绵延五百里,在和平环境中安逸了数百年的Z国又怎能抵挡的住Y国的军队.于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶.骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾.每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出

bzoj 1040 骑士

这题真不爽,各种WA,写个题解浏览器还挂了,真不爽. 所以不多说了,就说关于判断是否是父节点的问题,不能直接判,会有重边,这种情况只能用编号判,传进去入边的编号,(k^1) != fa,这样就可以了. 要注意的细节很多啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊! 上代码: #include <cstdio> #include <cstring> #include <cstdlib>

环套树

bzoj1040 骑士 题目大意:给出每个骑士的攻击力和痛恨的人,每个骑士不能和自己痛恨的人一起出现,选一些骑士使得他们的攻击力最大. 思路:因为每个骑士恨一个人,所以是一个环套树森林(注意n点n边的联通图可能是环套树森林!!!),虽然题目是单向边,但等同于双向边.对于环套树dp的做法,可以搜到环后,把环从一处断开,对于这道题目,对断开后的两点分别为根做树型dp(要求根不能选),加给答案.但是要注意二元环的情况,我们可以对于二元环只加一次无向边(其实是两条有向边),因为二元环完全可以当作有边相连

BZOJ 1040: [ZJOI2008]骑士 [DP 环套树]

传送门 题意:环套树的最大权独立集 一开始想处理出外向树树形$DP$然后找到环再做个环形$DP$ 然后看了看别人的题解其实只要断开环做两遍树形$DP$就行了...有道理! 然后洛谷时限再次不科学,卡常失败$SAD$ #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; co

[BZOJ 1040] [ZJOI2008] 骑士 【基环+外向树DP】

题目链接:BZOJ - 1040 题目分析 这道题目的模型就是一个图,不一定联通,每个连通块的点数等于边数. 每个连通块都是一个基环+外向树.即树上增加了一条边. 如果是树,就可以直接树形DP了.然而这是基环+外向树,需要先找到环上的一条边,记录这条边的两个端点 R1, R2,删掉这条边. 然后分两种情况:一定不选R1:一定不选R2:对这两种情况分别做一次树形DP就可以了. 答案加上这两种情况的答案的较大值. 代码 #include <iostream> #include <cstdli

BZOJ 1040: [ZJOI2008]骑士( 树形dp )

这是一个森林中, 每棵树上都有一个环...每棵树单独处理, 找出环上任意一条边断开, 限制一下这条边两端点的情况, 然后就可以树dp了.. ------------------------------------------------------------------------ #include<cstdio> #include<algorithm> #include<cstring> #include<cctype> using namespace