[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背包)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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\n",ans); 144 return 0; 145 }
Miemeng真的蒻