【HNOI2009】无归岛

题面

https://www.luogu.org/problemnew/show/P4410

题意:求仙人掌上最大独立集。

  1. $orzyyb$ $tarjan$中顺便$dp$的思路是在是太妙了。
  2. 同时用两个数组处理问题,减少思维复杂度(我jo的我写的代码总有一种暴力美学)。
  3. $g[x][0/1]$ 表示$x$及$x$的子树中,如果x选,最大独立集是多少。
  4. $f[x][0/1][0/1]$ 有意义当且仅当$x$在环上,并且不为环的头。 设$x$为环的头,$t$为环的尾,$f[y][0/1][0/1]$ 表示$y$ 选或不选,$t$选或不选,$y$及$y$的子树里的最大独立集是多少。
  5. 然后转移就可以了,注意要在$y$中扣除先前转移$t$的影响。
// luogu-judger-enable-o2
#include<cstdio>
#include<cstring>
#include<iostream>
#include<stack>
#include<vector>
#define INF 1000000007
#define ri register int
#define N 100500
using namespace std;

int n,tn,m;
int cir[N];
int a[N];
int ans;

inline int read() {
  int ret=0,f=0; char ch=getchar();
  while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar();
  while (ch>='0'&&ch<='9') ret*=10,ret+=ch-'0',ch=getchar();
  return f?-ret:ret;
}

struct graph {
  vector<int> to[N];
  int dfn[N],low[N],cnt;
  stack<int> s;
  int f[N][2][2],g[N][2];
    // state:t  state:x
  void add_edge(int a,int b) {
    to[a].push_back(b);
    to[b].push_back(a);
  }
  void tarjan(int x) {
    dfn[x]=low[x]=++cnt; s.push(x);
    g[x][1]=a[x];
    g[x][0]=0;
    for (ri i=0;i<to[x].size();i++) {
      int y=to[x][i];
      if (dfn[y]) low[x]=min(low[x],dfn[y]);
      else {
        tarjan(y);
        low[x]=min(low[x],low[y]);
        if (low[y]>=dfn[x]) {
          int t=s.top();
                    
          if (t==y) {
             g[x][1]+=max(g[y][0],0);
             g[x][0]+=max(max(g[y][1],g[y][0]),0);
             continue;
          }
                    
          int pret=t;
          s.pop();
                    
          f[t][1][1]=g[t][1];
          f[t][0][0]=g[t][0];
          f[t][1][0]=f[t][0][1]=-INF;
                    
          do {
            t=s.top(); s.pop();
            f[t][1][0]=g[t][1]-max(g[pret][0],0)+max(f[pret][0][0],0);
            f[t][0][0]=g[t][0]-max(max(g[pret][0],g[pret][1]),0)+max(max(f[pret][0][0],f[pret][1][0]),0);
            f[t][1][1]=g[t][1]-max(g[pret][0],0)+max(f[pret][0][1],0);
            f[t][0][1]=g[t][0]-max(max(g[pret][0],g[pret][1]),0)+max(max(f[pret][0][1],f[pret][1][1]),0);
            pret=t;
          }
          while (t!=y);
          g[x][1]+=max(f[y][0][0],0);
          g[x][0]+=max(max(max(f[y][0][0],f[y][0][1]),max(f[y][1][0],f[y][1][1])),0);
        }
        else {
          g[x][1]+=max(g[y][0],0);
          g[x][0]+=max(max(g[y][1],g[y][0]),0);
        }
      }
    }
  }
} G;

int main() {
  tn=n=read(); m=read();
  for (ri i=1;i<=m;i++) {
    int u=read(),v=read();
    G.add_edge(u,v);
  }
  for (ri i=1;i<=n;i++) a[i]=read();
  G.cnt=0;
  G.tarjan(1);
  int ans=0;
  for (ri i=1;i<=n;i++) ans=max(ans,max(G.g[i][0],G.g[i][1]));
  printf("%d
",ans);
}
原文地址:https://www.cnblogs.com/shxnb666/p/11080541.html