1. 程式人生 > 其它 >P2458 [SDOI2006]保安站崗

P2458 [SDOI2006]保安站崗

P2458 [SDOI2006]保安站崗&& #10157. 「一本通 5.2 例 5」皇宮看守

眼熟嗎?UVA1292 Strategic game#10156. 「一本通 5.2 例 4」戰略遊戲,最大獨立集問題,感覺是不是一模一樣?只不過是把邊權轉到點上了,做法一樣,你設 \(f[u][0]\) 為不選,\(f[u][1]\) 為選,則……不細說,於是你獲得了\(20\) 的好成績。

其實仔細一想還真不一樣,UVA和Loj10156 兩題要求覆蓋路徑,而這兩題是覆蓋點, 那就決定了前者只用考慮兩種決策,因為一條邊只可能被兩端的點覆蓋;後者則可能被自己、兒子、父親覆蓋。

於是設:\(f[u][0]\)

為被自己覆蓋,\(f[u][1]\) 為被父親覆蓋,\(f[u][2]\) 為被兒子覆蓋,則:

\[f[u][0]= \displaystyle \sum_{v \in son[u]} min(min(f[v][0],f[v][1]),f[v][2]) \] \[f[u][1]= \displaystyle \sum_{v \in son[u]} min(f[v][0],f[v][2]) \] \[f[u][2]=min(f[v][0]-f[v][2])(v\in son[u])+ \displaystyle \sum_{v \in son[u]} min(f[v][0],f[v][2]) \]

對於一式,顯然的,自己選不由子節點的狀態決策,只需取 \(min\)

即可。

對於二式,既然被父親覆蓋了,則節點的狀態只能由子節點選或者子節點被子節點的子節點選的 \(min\) 來更新。

對於三式,難理解,意思就是:如果節點被子節點覆蓋了,那子節點要麼自己覆蓋,要麼被子節點的子節點覆蓋,但是有一個特殊情況,就是必須在子節點中選一個最小的 \(f[v][0]\) 選上,因為如果子節點都是由 \(f[v][2]\) 轉移而來的,那麼 \(u\) 的子節點就意味著一個也不選,自己就沒人覆蓋,所以判斷這種情況並且要挑一個最小的換上。

/*
Knowledge : Rubbish Algorithm
Work by :Gym_nastics
Time : O(AC)
*/
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int INF=0x3f3f3f3f;
const int Mod=1e9+7;
const int N=1e6+6;

int read() {
    int x=0,f=0;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) f|=(ch=='-');
    for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch&15);
    return f?-x:x;
}

void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(x>9) print(x/10);
    putchar(x%10+48);
}

int fa[N],val[N],f[N][3],n,m;
int head[N],cnt;struct node{int v,nxt;}e[N];
void Add_edge(int u,int v){e[++cnt]=(node){v,head[u]};head[u]=cnt;}

void dfs(int u){
    f[u][0]=val[u];int Min=INF;bool flg=false;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].v;dfs(v);
        f[u][0]+=min(f[v][2],min(f[v][0],f[v][1]));
        f[u][1]+=min(f[v][0],f[v][2]);
        if(f[v][0]<f[v][2]){
            f[u][2]+=f[v][0];flg=true;
        } else{
            Min=min(Min,f[v][0]-f[v][2]);
            f[u][2]+=f[v][2];
        }
    }
    if(!flg) f[u][2]+=Min;
}
signed main() {
   n=read();int root=1;
   for(int i=1;i<=n;i++){
       int u=read(),k=read(),m=read();val[u]=k;
       for(int j=1;j<=m;j++){
           int v=read();Add_edge(u,v);
           fa[v]=u;
       }
   }while(fa[root])root=fa[root];dfs(root);
   print(min(f[root][0],f[root][2]));
   return 0;
}