标签:ons amp zjoi pac || play blank hid max
题目描述:
题解:
基环树+树形$dp$。
每次找到一个联通块,对于环上的每个点向树的方向做树形$dp$。
即$dp[i][0/1]$表示$i$点取/不取,$i$点子树内最大权。
$pj$难度?
然后拆环$dp$,讨论第一个点取还是不取。
代码:
#include<queue> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int N = 1000050; template<typename T> inline void read(T&x) { T f = 1,c = 0;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){c=c*10+ch-‘0‘;ch=getchar();} x = f*c; } int n,hed[N],cnt=1; ll w[N]; bool use[N]; struct EG { int to,nxt; }e[2*N]; void ae(int f,int t) { e[++cnt].to = t; e[cnt].nxt = hed[f]; hed[f] = cnt; } void bfs(int rt) { queue<int>q; q.push(rt); use[rt] = 1; while(!q.empty()) { int u = q.front(); q.pop(); for(int j=hed[u];j;j=e[j].nxt) { int to = e[j].to; if(!use[to]) { use[to] = 1; q.push(to); } } } } int sta[N],tl,rt; bool vis[N],cir[N]; int dfs0(int u,int pre) { if(vis[u]){rt=u;return 1;} vis[u] = 1; for(int j=hed[u],now;j;j=e[j].nxt) { int to = e[j].to; if(j==pre)continue; if((now=dfs0(to,j^1))) { if(now==1) { sta[++tl] = u; cir[u] = 1; if(u!=rt)return 1; } return 2; } } return 0; } ll dp[N][2],s[N][2]; void dfs(int u,int f) { dp[u][0] = 0,dp[u][1] = w[u]; for(int j=hed[u];j;j=e[j].nxt) { int to = e[j].to; if(to==f||cir[to])continue; dfs(to,u); dp[u][0]+=max(dp[to][0],dp[to][1]); dp[u][1]+=dp[to][0]; } } ll sol(int z) { s[1][0]=dp[sta[1]][0],s[1][1]=(z?dp[sta[1]][1]:0); for(int i=2;i<=tl;i++) { s[i][0]=dp[sta[i]][0]+max(s[i-1][0],s[i-1][1]); s[i][1]=dp[sta[i]][1]+s[i-1][0]; } return z?s[tl][0]:max(s[tl][0],s[tl][1]); } int main() { // freopen("tt.in","r",stdin); read(n); for(int i=1,f;i<=n;i++) { read(w[i]),read(f); ae(f,i),ae(i,f); } ll ans = 0; for(int i=1;i<=n;i++)if(!use[i]) { bfs(i);tl=0; dfs0(i,0); for(int j=1;j<=tl;j++)dfs(sta[j],0); ans+=max(sol(0),sol(1)); } printf("%lld\n",ans); return 0; }
标签:ons amp zjoi pac || play blank hid max
原文地址:https://www.cnblogs.com/LiGuanlin1124/p/10801620.html