1. 程式人生 > >ZJOI2008 騎士

ZJOI2008 騎士

str 方便 自己 ecn struct blank per max 找到

傳送門

這道題一開始看沒什麽頭緒……之後覺得不妨把騎士向自己痛恨的那個人連一條邊,那麽我們好像就轉化成了一個取父親就不能取兒子這麽一個操作。

非常的像那個沒有上司的舞會。

不過這題有一些bug,就是在一些聯通塊中可能存在環。不過我們仔細想一下之後會發現,因為每個點的出度都是1,所以如果騎士之間的厭惡情況成了一個環,那麽肯定是一個簡單環,也就是一個連一個,最後頭和尾相連的情況,否則就不滿足每個點的出度都是1了。同樣的,一個聯通塊中也不可能出現兩個及以上的環。

這樣我們找到所有的聯通塊中的環,之後把環上的任意一條邊斷開,之後就肯定成為了一棵樹。之後直接做樹型DP即可。不過註意這裏斷邊之後會出現兩個新的點,第一種情況是要強制性選其中之一,第二種情況就強制性選另一個,直接在上面進行樹型DP即可。

因為圖不一定是完全聯通的,所以我們像tarjan一樣枚舉每一個點dfs,如果這個點被搜過,那麽我們直接跳過。最後把所有聯通塊內部的答案加起來就是最終的結果。

註意這裏求一個聯通塊的環……因為後面DP方便我們還是選擇了建立無向圖。之後就跟著dalao學了一手怎麽用位運算判斷環……

我們像跑網絡流的時候一樣,把ecnt的初始值設為-1,這樣加入的兩條邊就可以通過^1來進行直接轉移。之後在每次繼續向下dfs的時候,把上次走的那條邊也一起傳下去,這樣就可以如果當前的邊^1是上一條邊,就說明這條邊已經走過了,那麽直接繼續。然後如果找到了環,我們記錄一下兩個端點的位置之後開始DP就可以了。

然後在DP的時候,如果當前邊或者其^1的值是你斷過的那條邊的邊號,就直接繼續。

註意find的時候初始要傳-2,因為-1^1 = -2.

具體細節看一下代碼。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar(‘\n‘)
using
namespace std; typedef long long ll; const int M = 1000005; ll n,s,tot,x1,x2,edg,dp[M][2],q[M],head[M],a,b,ans,ecnt = -1; bool vis[M]; ll read() { ll ans = 0,op = 1; char ch = getchar(); while(ch < 0 || ch > 9) { if(ch == -) op = -1; ch = getchar(); } while(ch >=0 && ch <= 9) { ans *= 10; ans += ch - 0; ch = getchar(); } return ans * op; } struct node { ll next,to; }e[M<<1]; void add(ll x,ll y) { e[++ecnt].to = y; e[ecnt].next = head[x]; head[x] = ecnt; } void find(ll x,ll pre) { vis[x] = 1; for(int i = head[x];i != -1;i = e[i].next) { if((i^1) == pre) continue; if(vis[e[i].to]) { x1 = x,x2 = e[i].to,edg = i; continue; } find(e[i].to,i); } } void dfs(ll x,ll pre) { dp[x][0] = 0,dp[x][1] = q[x]; for(int i = head[x];i != -1;i = e[i].next) { if((i^1) == pre) continue; if(i == edg || (i^1) == edg) continue; dfs(e[i].to,i); dp[x][1] += dp[e[i].to][0]; dp[x][0] += max(dp[e[i].to][1],dp[e[i].to][0]); } } int main() { memset(head,-1,sizeof(head)); n = read(); rep(i,1,n) a = read(),b = read(),add(i,b),add(b,i),q[i] = a; ans = 0; rep(i,1,n) { if(vis[i]) continue; find(i,-2); dfs(x1,-1); ll cur = dp[x1][0]; dfs(x2,-1); cur = max(cur,dp[x2][0]); ans += cur; } printf("%lld\n",ans); return 0; }

ZJOI2008 騎士