[BZOJ1040][ZJOI2008]骑士

[ZJOI2008]骑士

Description
Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的Z国又怎能抵挡的住Y国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出征的。战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力。为了描述战斗力,我们将骑士按照1至N编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。
Input
NN
Output
应包含一行,包含一个整数,表示你所选出的骑士军团的战斗力。
Sample Input
3
102
203
301
Sample Output
30
HINT
对于100%的测试数据,满足 N 1000000,每名骑士的战斗力都是不大于 1000000 的正整数。

Solution :
这道题本质上是要我们求一个环套树森林的最大点权独立集。
我们先来一发 Tarjan 算法得到所有的环。
然后我们考虑一个环,随意选取一条边,将它破开为链,那么分别dp边两端端点,取它们均强制不取的状态的更优解计入答案。

Code :

/*************************************************************************
    > File Name: bzoj1040.cpp
    > Author: Archer
 ************************************************************************/

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace std;

const int N = 1111111;
typedef long long ll;
#define tst puts("OK")
struct Node{ int v, flag; Node *nxt, *twin; }pool[N << 1], *tail = pool, *g[N];
int n, low[N], dfn[N], dfsnum;
vector<vector<int> > circle;
bool flag[N][2];
ll f[N][2], ans, w[N];

inline int read(){
    int x = 0, f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = 0; ch = getchar();}
    while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    return f ? x : -x;
}

vector<int> tmp;
stack<int> st;
inline void dfs(int u){
    st.push(u); dfn[u] = low[u] = ++dfsnum;
    for (Node *p = g[u]; p; p = p->nxt) if (p->flag){
        p->flag = p->twin->flag = 0;
        if (!dfn[p->v]){ dfs(p->v); low[u] = min(low[u], low[p->v]); }
        low[u] = min(low[u], dfn[p->v]);
    }
    if (low[u] == dfn[u]){ tmp.clear();
        if (st.top() == u) {st.pop(); return;}
        int v; do{ tmp.push_back( v = st.top()); st.pop();} while (v != u);
        circle.push_back(tmp);
    }
}
inline void getcircles(){ for (int i = 1; i <= n; i++) if (!dfn[i]) dfs(i); }
inline void dp(int u, int no){
    flag[u][no] = 1; f[u][0] = 0; f[u][1] = w[u];
    for (Node *p = g[u]; p; p = p->nxt) if (!flag[p->v][no]){
        dp(p->v, no); f[u][1] += f[p->v][0]; f[u][0] += max(f[p->v][0], f[p->v][1]);
    }
}
int main(){
    n = read();
    for (int i = 1; i <= n; i++){
        w[i] = 1ll * read(); int v = read();
        tail->flag = 1; tail->v = v; tail->nxt = g[i]; tail->twin = tail + 1; g[i] = tail++;
        tail->flag = 1; tail->v = i; tail->nxt = g[v]; tail->twin = tail - 1; g[v] = tail++;
    }
    getcircles(); 
    for (int i = 0; i < circle.size(); i++){
         dp(circle[i][0], 0); ll tmp1 = f[circle[i][0]][0];
         dp(circle[i][1], 1); ll tmp2 = f[circle[i][1]][0];
         ans += max(tmp1, tmp2);
    }
    printf("%lld\n", ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值