【初识基环树】gay环树?
衢州饭店出锅了?以为自己幸运,哪知无法逃过一劫。学个OI,菊花不保啊(雾。最近考了好多道关于基环树(包括基环内向树基环 外向树)的题,趁机学一波
首先明确,基环树不是树。其是一颗树+一条额外的边,即一颗树+一个环。大致简述一下这种特殊结构出现的主要原因就是n个点连出去n条边,而树是n点n-1条边,这会恰好构成一个环(由于是n个点n条边,所以,有且仅有一个!)。
但为什么我们称其为“树”呢?主要就是我们只要略加修改其就可以当作一颗树来做图论(动归)等等,而树由于其优越的结构-->两点间仅有一条路径,可以完成很多事情。
将基环树拆分开成环和树两部分处理,重要的就是环这一部分。我们发现,如果我们将基环树环的一条边切开,那么这就构成了一颗树!我们可以很好的将这个切边的方法进行利用。
一道基环树板子题 bzoj1040.
简述:n点n边,一个点选就不能选和他相邻的点,每个点有权值,求选出的最大权值。
原题虽然看似是有向边事实上一个人讨厌另一个人,那么两人只能选其一。由于不满足两两之间一定到达,他就构成了一个基环树森林。
如果是树 DP,DP方程同上司的舞会;dp[i][0]不选这个点其子树最大权值,dp[i][1]选这个点其子树最大权值
dp[i][0] += max(dp[son][1],dp[son][0]) dp[i][1] = val[i] += dp[son][0]
对于每一颗基环树我们考虑切割一条边。然后分别从这两个切割的点当根开始跑DP,由于从这两个点跑出去的DP不受限制,可能会出现选择了对方点的情况,所以我们只能各取一方的dp[x][0]取一个最大值加入答案。
code:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn = 2000005; int n; int jpoint1,jpoint2,jzb; int en[maxn],nt[maxn],la[maxn],owo=1; long long val[maxn]; void addedge(int a,int b) { en[++owo] = b; nt[owo]=la[a]; la[a]=owo; } long long dp[maxn][2];//dp[i][0] xuan dp[i][1] buxuan bool vis[maxn]; void dfs(int x,int ba) { vis[x] = 1; bool flag = 1; for(int it=la[x];it;it=nt[it]) { if(en[it]==ba&&flag) { flag = 0; continue;} if(vis[en[it]]) { jpoint1 = en[it]; jpoint2 = x; jzb = it; // flag = 1; } else dfs(en[it],x); } } void getdp(int x,int ba) { dp[x][0] = 0; dp[x][1] = val[x]; for(int it=la[x];it;it=nt[it]) { if(en[it]==ba||(it==jzb)||(it==(jzb^1))) continue; getdp(en[it],x); dp[x][0] += max(dp[en[it]][0],dp[en[it]][1]); dp[x][1] += dp[en[it]][0]; } } long long ans; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { int y; scanf("%I64d%d",&val[i],&y); addedge(i,y); addedge(y,i); } for(int i=1;i<=n;i++) { if(!vis[i]) { long long aha = 0; dfs(i,0); getdp(jpoint1,0); aha = max(dp[jpoint1][0],aha); getdp(jpoint2,0); aha = max(dp[jpoint2][0],aha); ans+=aha; } } printf("%I64d",ans); }基环内向树:基环内向树事实上是一种有向图。其结构模样和基环树一样,只是他作为有向图所有树上边都往环上走,也就是说任何在基环内向树上任意点出发都可以到达环上。 内向树得题:ZROI花园 基环外向树:和内向树很像,只是这里是环上和树上的点都能向外到树的叶子结点上。