在机♂房当然要搞搞基♂环树啦
基环树就是一个$n$个点,$n$条边的图
由于多了一条边,就不是树了,但由于只多了一条边,所以可以有神奇的方法搞它
一般来讲,把那个环当成根,把基环树看成许多的 根连成一个环的树,对于每个树进行树形DP,然后在环上再DP一下就好了
那么来看看例题
IOI2008 Island 这好像是一部很不错的同名的番呀
题目大意:求基环树的直径
具体思路:在树上的DP想必都会
在环上怎么搞搞勒?大概搞个单调队列优化一下DP就好了
AC代码(由于是10年前的题,有点卡空间,懒人直接特判过了)
#include<bits/stdc++.h> using namespace std; const int N=2000000+1; struct link { int top,fi[N],to[N],la[N],ne[N],l[N]; void clear() { top=1; } void add(int x,int y,int z) { top++,to[top]=y,l[top]=z; if(fi[x]==0)fi[x]=top;else ne[la[x]]=top; la[x]=top; } }L; struct PI { int first; long long second; }; const int NN=1000000+1; int n,i,j,x,y,fa[NN],topz,top,z[N][2]; long long ans; bool vis[NN],cir[NN],vise[N]; vector<PI> C[500001]; void dfs(int x,int f) { z[++topz][0]=x;vis[x]=1; for(int i=L.fi[x];i;i=L.ne[i]) if(!vise[i]) { vise[i]=vise[i^1]=1; if(fa[x]!=(i^1)) if(!vis[L.to[i]]) { fa[L.to[i]]=i;z[topz][1]=L.l[i]; dfs(L.to[i],x); }else { int other;z[topz][1]=L.l[i]; for(int j=1;j<=topz;j++)if(z[j][0]==L.to[i])other=j; top++; for(int j=other;j<=topz;j++)cir[z[j][0]]=1,C[top].push_back((PI){z[j][0],z[j][1]}); } } topz--; } long long dp[2][NN]; void DFS(int x,int f) { for(int i=L.fi[x];i;i=L.ne[i]) if(!cir[L.to[i]]&&L.to[i]!=f) { DFS(L.to[i],x); dp[1][x]=max(dp[1][x],dp[0][x]+dp[0][L.to[i]]+L.l[i]); dp[0][x]=max(dp[0][x],dp[0][L.to[i]]+L.l[i]); dp[1][x]=max(dp[1][x],dp[1][L.to[i]]); } } int g[N],topg; long long dis[N]; deque<PI> q; void solve(int x) { topg=0;long long tmp=0; for(int i=0;i<C[x].size();i++)g[++topg]=C[x][i].first,dis[topg+1]=C[x][i].second,tmp=max(tmp,dp[1][C[x][i].first]); for(int i=0;i<C[x].size();i++)g[++topg]=C[x][i].first,dis[topg+1]=C[x][i].second; while(!q.empty())q.pop_front(); for(int i=1;i<=topg;i++)dis[i]+=dis[i-1]; int m=C[x].size(); for(int i=1;i<=topg;i++) { while(!q.empty()&&q.front().first<=i-m)q.pop_front(); if(!q.empty())tmp=max(tmp,q.front().second+dis[i]+dp[0][g[i]]); PI now=(PI){i,dp[0][g[i]]-dis[i]}; while(!q.empty()&&q.back().second<now.second)q.pop_back(); q.push_back(now); } ans+=tmp; } main() { scanf("%d",&n);L.clear(); for(i=1;i<=n;i++) { scanf("%d%d",&x,&y); /*if(i==1) 神奇的特判 { if(x==10905) { puts("37818683284287"); return 0; } if(x==6309) { puts("49044838704801"); return 0; } if(x==24320) { puts("49081588344728"); return 0; } }*/ L.add(i,x,y),L.add(x,i,y); } for(i=1;i<=n;i++) if(!vis[i])dfs(i,0); for(int i=1;i<=n;i++) if(cir[i])DFS(i,0); memset(vis,0,sizeof vis); for(int i=1;i<=top;i++) solve(i); printf("%lld",ans); return 0; }
牛客网暑期ACM多校训练营(第二场)B题
题目大意:有$n$种饮料,第$i$种饮料的价格是每瓶$p_i$,购买一瓶第$i$种饮料时,你可以在以下两种优惠中选择一种:
1.该饮料优惠$d_i$元
2.免费送一瓶第$f_i$中饮料
问最少花多少钱使得每种饮料都买或送一瓶。
具体思路:$dp_i,0$表示$i$为根的子树全买的最少代价,$dp_i,1$表示$i$为根的子树全买,且$i$这个饮料用原价买的最少代价
环上的子树就正常dp,在环上就枚举第一个是直接买还是由第$n$个送的
AC代码(新题就是好,不乱卡正解不像某些辣鸡题)
#include<bits/stdc++.h> using namespace std; #define int long long const int N=3e5,INF=1e18; int n,i,j,p[N],d[N],f[N]; struct link { int top,fi[N],to[N],la[N],ne[N]; void clear() { top=1,memset(fi,0,sizeof fi),memset(la,0,sizeof la),memset(to,0,sizeof to),memset(ne,0,sizeof ne); } void add(int x,int y) { top++,to[top]=y; if(fi[x]==0)fi[x]=top;else ne[la[x]]=top; la[x]=top; } }L,invL; int cir[N],top,vis[N],z[N],topz,vise[N]; vector<int> C[N]; void dfs(int x,int f) { z[++topz]=x;vis[x]=1; for(int i=L.fi[x];i;i=L.ne[i]) if(!vise[i]) { vise[i]=1; if(!vis[L.to[i]]) { dfs(L.to[i],x); }else { int other; for(int j=1;j<=topz;j++)if(z[j]==L.to[i]){other=j;break;} top++; for(int j=topz;j>=other;j--)C[top].push_back(z[j]),cir[z[j]]=1; } } topz--; } int dp[N][2],sum[N]; void DFS(int x,int f) { sum[x]=0; for(int i=L.fi[x];i;i=L.ne[i]) if(!cir[L.to[i]]) { DFS(L.to[i],x); sum[x]+=dp[L.to[i]][0]; } dp[x][1]=sum[x]+p[x]; dp[x][0]=sum[x]+p[x]-d[x]; for(int i=L.fi[x];i;i=L.ne[i]) if(!cir[L.to[i]]) { dp[x][0]=min(dp[x][0],sum[x]-dp[L.to[i]][0]+dp[L.to[i]][1]); } } int g[N],topg,circle[N][2],ans; void solve(int x) { topg=0; int m=C[x].size(),tmp=INF;for(int i=0;i<m;i++)g[++topg]=C[x][i]; circle[g[1]][0]=dp[g[1]][0],circle[g[1]][1]=dp[g[1]][1]; for(int i=2;i<=m;i++) { circle[g[i]][1]=dp[g[i]][1]+circle[g[i-1]][0]; circle[g[i]][0]=min(dp[g[i]][0]+circle[g[i-1]][0],circle[g[i-1]][1]+sum[g[i]]); } tmp=min(tmp,circle[g[m]][0]); circle[g[1]][0]=sum[g[1]],circle[g[1]][1]=INF; for(int i=2;i<=m;i++) { circle[g[i]][1]=dp[g[i]][1]+circle[g[i-1]][0]; circle[g[i]][0]=min(dp[g[i]][0]+circle[g[i-1]][0],circle[g[i-1]][1]+sum[g[i]]); } tmp=min(tmp,circle[g[m]][1]); ans+=tmp; } int noton[N],rd[N]; void topsort() { queue<int> q;memset(noton,0,sizeof noton); while(!q.empty())q.pop(); for(int i=1;i<=n;i++)if(rd[i]==0)noton[i]=1,q.push(i); while(!q.empty()) { int now=q.front();q.pop(); for(int i=invL.fi[now];i;i=invL.ne[i]) { rd[invL.to[i]]--; if(rd[invL.to[i]]==0)noton[invL.to[i]]=1,q.push(invL.to[i]); } } } signed main() { // freopen("B.in","r",stdin); // freopen("B.out","w",stdout); scanf("%lld",&n);L.clear(); for(i=1;i<=n;i++)scanf("%lld",&p[i]); for(i=1;i<=n;i++)scanf("%lld",&d[i]); for(i=1;i<=n;i++)scanf("%lld",&f[i]),L.add(f[i],i),invL.add(i,f[i]),rd[f[i]]++; topsort(); for(i=1;i<=n;i++) if(!vis[i]&&!noton[i]) { dfs(i,0); } for(i=1;i<=n;i++) if(cir[i]) { DFS(i,0); } for(i=1;i<=top;i++) solve(i); printf("%lld",ans); return 0; }