洛谷P2607 [ZJOI2008]骑士

13 April 2019 | duanyll | Tags: OI 题解 DP

经典题,n个点n条边的图一定是一个基环树森林,于是可以先DFS一遍把环找出来,再断环(在环上随便找一条边,强制两个端点之一不选),就变成了一个没有上司的舞会了。

\[dp[u][0] = \sum_{v}max(dp[v][0],dp[v][1])\] \[dp[u][1] = \sum_{v}dp[v][0]\]
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;

typedef long long int64;

const int MAXN = 1e6+10;
const int INF = 0x3f3f3f3f;

class lfs {
   public:
   	int w[MAXN];
    lfs(int N) {
        memset(head, -1, sizeof head);
        memset(e,0,sizeof e);
        ecnt = 0;
        n = N;
    }
    void adde(int from, int to) {
        e[ecnt].to = to;
        e[ecnt].next = head[from];
        head[from] = ecnt++;
    }
    void addde(int a, int b) {
        adde(a, b);
        adde(b, a);
    }
    
    int64 solve(){
    	memset(vis,false,sizeof vis);
    	memset(dp,0,sizeof dp);
    	int64 ans = 0;
    	for(int i = 1;i<=n;i++){
    		if(!vis[i]){
    			dfs1(i,0);
    			dfs2(e[edge_to_break].to,0);
    			int64 now = dp[e[edge_to_break].to][0];
    			dfs2(e[edge_to_break^1].to,0);
    			now = max(now,dp[e[edge_to_break^1].to][0]);
    			ans += now;
    		}
    	}
    	return ans;
    }

   protected:
    struct Edge {
        int to, next;
    } e[MAXN * 2];
    int head[MAXN];
    int ecnt;
    int n;

   private:
   	bool vis[MAXN];
   	int64 dp[MAXN][2];
   	int edge_to_break;
   	
   	void dfs1(int u,int fa) {
   		vis[u] = true;
   		for (int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].to;
            if (v != fa) {
            	if(vis[v]){
            		edge_to_break = i;
            		continue;   // <============!!!
            	}
            	dfs1(v,u);
            }
        }
   	}
   	
    void dfs2(int u, int fa) {
    	dp[u][0] = 0;
    	dp[u][1] = w[u];
        for (int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].to;
            if (v != fa && i != edge_to_break && (i^1) != edge_to_break) {
                dfs2(v, u);
                dp[u][0] += max(dp[v][0],dp[v][1]);
                dp[u][1] += dp[v][0];
            }
        }
//        clog << "dp[" << u << "][0] = " << dp[u][0] << endl;
//        clog << "dp[" << u << "][1] = " << dp[u][1] << endl;
    }
};

#include <cctype>
#include <cstdio>

inline int read() {
    int X = 0, w = 0;
    char ch = 0;
    while (!isdigit(ch)) {
        w |= ch == '-';
        ch = getchar();
    }
    while (isdigit(ch)) {
        X = (X << 3) + (X << 1) + (ch ^ 48);
        ch = getchar();
    }
    return w ? -X : X;
}

int main(){
//	freopen("data","r",stdin);
    int n = read();
    lfs* tree = new lfs(n);
    for(int i = 1;i<=n;i++){
        tree->w[i] = read();
        tree->addde(i,read());
    }
    cout << tree->solve() << endl;
}

有一个细节问题是标记处的continue,如果改为return就会引起无限递归爆栈,不知道为什么。