[BZOJ2427][HAOI2010]软件安装-tarjan缩点-树上dp

<题面>

这个题真伤人

之前Tarjan和树规都没学好,吃了不少亏,仔仔细细的搞了一天,收获颇丰

先来一个Tarjan的链接:$mathbb{O}$

题目的数据比较友好:

$dp$不对:$leq10$

$dp$对了Tarjan不对:$40$

都对了:$100$


接下来就是思路

首先观察题目。

$n$个点$n$条边,也许有环。

所以先以$0$为根节点(虚根,可以想象成”系统“,所有软件的依赖,无价值无内存),这样有一个好处,不用建边时特判,直接建就好了(无依赖是$0$)

额,你要问我怎么建,从被依赖的向依赖的指一个有向边

被依赖的$Longrightarrow$依赖的,这样建好的图便于转移

建图结束,下面是个重头戏:缩点

这个题缩点极为简单,你会发现,环里的点已经互相依赖,不可能依赖其他的程序(除了虚根,我们暂不考虑)

这是我们就可以把它们打包安装。

首先Tarjan找出这个环,打个标记(我用的用新节点下标)

这样,我们把所有边扫一遍,凡是从环中的点出发的边起点都拽到新节点上,顺便把价值也统过去(别记重了)

最后把新节点连到虚根上。

还是很棒的吧?

然后是$dp$,就是很普通的树上背包。

$f_{i,j}$表示第$i$号节点使用$j$空间时的最大价值

写式子为敬:

$f_{i,j} = max limits_{w leq j , s in son_i} { f_{i,j-w}+f_{s,w} }$

要从大往小$dp$,防止重复更新(01背包)

  1 #include <iostream>
  2 #include <cstring>
  3 #include <cstdio>
  4 #define  N 111
  5 #define  M 555
  6 using namespace std;
  7 
  8 struct STAR{
  9     int f,t,next;
 10 }rs[N*N];int fl[2*N],cnt=0;
 11 void add(int f,int t){
 12     rs[cnt].f=f;
 13     rs[cnt].t=t;
 14     rs[cnt].next=fl[f];
 15     fl[f]=cnt;
 16     cnt++;
 17 }
 18 int pom,rom;
 19 int val[2*N],cost[2*N];
 20 struct mystack{
 21     int st[2*N],tp;
 22     mystack(){
 23         tp=0;
 24         memset(st,0,sizeof st);
 25     }
 26     int top(){
 27         return st[tp-1];
 28     }
 29     void pop(){
 30         tp--;
 31     }
 32     void push(int k){
 33         st[tp]=k;
 34         tp++;
 35     }
 36     bool empty(){
 37         if(tp==0)return true;
 38         return false;
 39     }
 40 }sk;
 41 void prerun(){
 42     memset(fl,-1,sizeof fl);
 43 }
 44 int dfn[2*N],low[2*N],dep=0,bl[2*N];
 45 bool is_in[2*N],is_v[2*N],cut[2*N];
 46 void tarjan(int k){//cout<<" J "<<k<<endl;
 47     dep++;
 48     dfn[k]=low[k]=dep;
 49     int t;
 50     sk.push(k);
 51     is_in[k]=1;
 52     for(int i=fl[k];i!=-1;i=rs[i].next){
 53         t=rs[i].t;
 54         if(!dfn[t]){
 55             tarjan(t);
 56             low[k]=min(low[k],low[t]);
 57         }
 58         else{
 59             if(is_in[t]){
 60                 low[k]=min(low[k],low[t]);
 61             }
 62         }
 63     }
 64     if(low[k]==dfn[k]){
 65         if(sk.top()==k){
 66             sk.pop();
 67             is_in[k]=0;
 68             return;
 69         }
 70         int p=0;
 71         pom++;
 72         do{
 73             p=sk.top();
 74             is_in[p]=0;
 75             bl[p]=pom;
 76             sk.pop();
 77         }while(p!=k);
 78     }
 79 }
 80 int dp[2*N][M];
 81 void dfs(int k){
 82     is_v[k]=1;
 83     dp[k][0]=0;
 84     for(int i=fl[k];i!=-1;i=rs[i].next){
 85         int t=rs[i].t;
 86         if(!is_v[t]){
 87             dfs(t);
 88             for(int w=rom;w>=0;w--){
 89                 for(int m=0;m<=rom;m++){
 90                     if(w-m>=0)
 91                         dp[k][w]=max(dp[k][w],dp[k][w-m]+dp[t][m]);
 92                 }
 93             }
 94         }
 95     }
 96     for(int i=rom;i>=0;i--)
 97         if(i-cost[k]>=0)
 98             dp[k][i]=dp[k][i-cost[k]]+val[k];
 99         else 
100             dp[k][i]=0;
101 }
102 int ans=0;
103 int main(){
104     int a,beg;
105     prerun();
106     scanf("%d%d",&pom,&rom);beg=pom;
107     for(int i=1;i<=pom;i++)
108         scanf("%d",&cost[i]);
109     for(int i=1;i<=pom;i++)
110         scanf("%d",&val[i]);
111     for(int i=1;i<=pom;i++){
112         scanf("%d",&a);
113         add(a,i);
114     }
115     for(int i=0;i<=beg;i++){
116         if(!dfn[i])tarjan(i);
117     }
118     for(int i=0;i<cnt;i++){
119         int fo=rs[i].f,to=rs[i].t;
120         if(bl[fo]!=0){//cout<<"Cut"<<rs[i].f<<endl;
121             if(cut[fo]==0){
122                 val[bl[fo]]+=val[fo];
123                 cost[bl[fo]]+=cost[fo];
124                 cut[fo]=1;
125             }
126             add(bl[fo],to);
127             
128         }
129     }
130     for(int i=1;i<=beg;i++){
131         if(bl[i]!=0){
132             is_v[i]=1;
133             fl[i]=-1;
134         }
135     }
136     for(int i=beg+1;i<=pom;i++){
137         add(0,i);
138     }
139     dfs(0);
140     for(int i=0;i<=rom;i++){
141         ans=max(ans,dp[0][i]);
142     }
143     printf("%d
",ans);
144     return 0;
145 }
Code
Miemeng真的蒻
原文地址:https://www.cnblogs.com/kalginamiemeng/p/11180393.html