【jzoj3773】小P的烦恼
题目描述
小 P 最近遇上了大麻烦,他的高等代数挂科了。于是他只好找高代老师求情。善良的高代老师答应不挂他,但是要求小 P 帮助他一起解决一个难题。
问题是这样的,高代老师近期要组织班上同学一起去漂流,漂流可以看做是在一张 n 个点 m 条边的有向无环图上进行的,点编号从 0 到 n-1 ,表示景点; 边是连接各景点的一定长度的河道。同时,定义编号为 s 是起点而 t 是终点。我们不妨把从 s 点到 t 点不论走什么样的路径都需要经过的边称为桥, 这些桥由于地势险要所以是危险的。现在高代老师有两条长度为 l 的安全绳,他希望用这两条安全绳覆盖尽可能长的桥,使得他们通过的桥的长度之和尽量短。
输入
本题包含多组数据,第一行是一个数 T(T<=5)表示数据组数,对于每组数据,第一行是 5 个数,n,m,s,t,l。
以下 m 行,每行包括三个数 si,ti,pi,表示从 si 到 ti 有一条长度为 pi 的单向边。
输出
对于每组数据,输出一行,包括一个整数,为最优情况下通过的桥的长度之和。如果不存在从 s 到 t 的路径,请输出-1。
输入样例
1
8 9 0 7 7
0 4 1
0 1 10
1 2 9
4 2 2
2 5 8
4 3 3
5 6 6
5 6 7
6 7 5
输出样例
1
数据范围
对于 10%的数据,n<=10,m<=20
对于 30%的数据,n<=1000,m<= 10000
对于 100%的数据,n<=100000, m<=200000,0<=s,t,si,ti<n, l<=10^9,pi<=1000
思路
emmmmmmm当时这是第三题,第一题用奇怪的方法水过了,第二题一看推不出式子,就不弄啦啦啦
这题,一看应该能分成两步。
1、找桥
2、找最大覆盖方案
找桥?
有关连通性的tarjan算法可以解决。
但我考场上不会。
再一看,有向无环图,有戏,可以考虑乱搞。首先想到拓扑,桥的起点和终点至少应是拓扑序相邻的。但相邻的怎样才是桥呢?画个数轴吧。
不是桥的那些边堆积在一起,而桥的位置只有一条边。好,我们现在需要找出相邻的,只有一条边覆盖的位置。在拓扑序上,一条边覆盖一个区间,可以用差分处理每条边就能求出覆盖数了。找桥完毕。
找最大覆盖方案?
在当时,我没有想出来,打了的找桥也相当于是废了……
两条绳子不好弄吧?
简化
一条绳子怎么弄?
再建一个数轴,从起点到终点,非桥、桥交错分布,非桥部分可以预处理最短路。
绳子结尾位置确定了,那覆盖的长度就确定了。绳子结尾在哪?
分情况讨论
所以,绳子结尾在一个区间内可以等价为在一个区间的结尾。
如何确定覆盖长度?
用双指针可以扫出绳子能往前延伸至哪个区间,用前缀和就能求答案啦。
回归
两条绳子怎么弄?
如果两条绳子完全分属不同的区间,那么枚举,在一条绳子的基础上,加上这条绳子起点前一条绳子能覆盖的最大值就行了吧。
如果后面绳子的头接着前面绳子的尾呢?
仍等价于两倍长度的绳子!
记f[i][j]为,以1到i号区间为结尾,j条绳子能覆盖的最大值;预处理的g[i][j]为以i号区间为结尾,长度为j的绳子能覆盖多长;pre[i]为以i号区间为结尾的绳子的起点。
最后的方程为
f[i][1]=min(f[i-1][1],g[i][l]);
f[i][2]=min(f[i-1][2],f[pre[i]-1][1]+g[i][l],g[i][2l]).
找桥的其他方案
我这个蒟蒻自己想到了这些方法,秉着分享思路的理念写了这个玩意。我在此不打算写有关tarjan的内容。
针对有向无环图,从起点到桥的起点的方案数*桥的终点到终点的方案数=从起点到终点的总方案数。可根据此点找桥。
代码
WA,这种傻方法,我写的这么长,就不放了吧QWQ
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cmath> 5 #include <algorithm> 6 #include <cstring> 7 #include <string> 8 #include <queue> 9 using namespace std; 10 11 struct line 12 { 13 int to,next,w; 14 }e[200050]; 15 16 int fl[100050],enfl[100050],inp[100050],outp[100050],hd[100050],q[100050],srt[100050],brst[100050],bren[100050],brw[100050]; 17 int t,n,m,st,en,i,j,k,x,y,z,l,tot,head,tail,cntzero,brtot,srtot,etot; 18 int ek[400050],sumk[400050],ef[400050],sumf[400050]; 19 int dp[400050][3],pre[400050][2]; 20 21 void ins(int p,int s,int r) 22 { 23 tot++; e[tot].to=s; e[tot].next=hd[p]; e[tot].w=r; hd[p]=tot; 24 inp[s]++; outp[p]++; 25 } 26 27 void dfs(int p) 28 { 29 fl[p]=1; 30 if (p==en) return; 31 int i1=hd[p]; 32 while (i1!=-1) 33 { 34 if (fl[e[i1].to]==0) dfs(e[i1].to); 35 if (enfl[e[i1].to]==1) enfl[p]=1; 36 if (enfl[e[i1].to]==0) {outp[p]--; inp[e[i1].to]--;} 37 i1=e[i1].next; 38 } 39 } 40 41 void tuopu() 42 { 43 brtot=0; 44 memset(q,0,sizeof(q)); 45 memset(fl,0,sizeof(fl)); 46 memset(srt,0,sizeof(srt)); 47 head=0; tail=0; srtot=0; 48 for (i=0;i<=n-1;i++) 49 if (enfl[i]==1) 50 if (inp[i]==0) {q[tail]=i; tail++; } 51 while (head<tail) 52 { 53 i=q[head]; 54 srtot++; srt[srtot]=i; fl[i]=srtot; 55 j=hd[i]; 56 while (j!=-1) 57 { 58 if (enfl[e[j].to]==1) 59 { 60 inp[e[j].to]--; outp[i]--; 61 if (inp[e[j].to]==0) {q[tail]=e[j].to; tail++;} 62 } 63 j=e[j].next; 64 } 65 head++; 66 } 67 } 68 69 void findbridge() 70 { 71 memset(q,0,sizeof(q)); 72 memset(inp,0,sizeof(inp)); 73 for (i=0;i<=n-1;i++) 74 if (enfl[i]==1) 75 { 76 j=hd[i]; 77 while (j!=-1) 78 { 79 if (enfl[e[j].to]==1) 80 { 81 q[fl[i]-1]++; q[fl[e[j].to]-1]--; 82 } 83 j=e[j].next; 84 } 85 } 86 87 inp[0]=q[0]; 88 for (i=1;i<=srtot;i++) inp[i]=inp[i-1]+q[i]; 89 90 for (int i1=1;i1<=srtot;i1++) 91 { 92 i=srt[i1]; 93 if (enfl[i]==1) 94 { 95 j=hd[i]; 96 while (j!=-1) 97 { 98 if (enfl[e[j].to]==1) 99 { 100 if ((fl[e[j].to]-fl[i]==1)&&(inp[fl[i]-1]==1)) 101 { 102 brtot++; brst[brtot]=i; bren[brtot]=e[j].to; brw[brtot]=e[j].w; 103 } 104 } 105 j=e[j].next; 106 } 107 } 108 } 109 } 110 111 int sp(int p,int r) 112 { 113 int i1,j1,k1; 114 for (i1=fl[p];i1<=fl[r];i1++) q[i1]=1<<30; 115 q[fl[p]]=0; 116 for (i1=fl[p];i1<=fl[r]-1;i1++) 117 { 118 j1=srt[i1]; k1=hd[j1]; 119 while (k1!=-1) 120 { 121 if (enfl[e[k1].to]==1) q[fl[e[k1].to]]=min(q[fl[e[k1].to]],q[i1]+e[k1].w); 122 k1=e[k1].next; 123 } 124 } 125 return q[fl[r]]; 126 } 127 128 void countt() 129 { 130 if (brtot==0) {cout<<0<<endl; return;} 131 sumk[0]=0; sumf[0]=0; 132 for (i=1;i<=brtot-1;i++) 133 { 134 ek[2*i-1]=brw[i]; ek[2*i]=sp(bren[i],brst[i+1]); 135 ef[2*i-1]=brw[i]; ef[2*i]=0; 136 } 137 ek[2*brtot-1]=brw[brtot]; ef[2*brtot-1]=brw[brtot]; 138 for (i=1;i<=2*brtot-1;i++) {sumk[i]=ek[i]+sumk[i-1]; sumf[i]=ef[i]+sumf[i-1];} 139 int ll,rr,sum1,sum2; 140 sum1=0; ll=1; rr=1; 141 while (rr<=2*brtot-1) 142 { 143 sum1+=ek[rr]; 144 while (sum1>l) 145 { 146 sum1-=ek[ll]; ll++; 147 } 148 pre[rr][0]=ll; 149 rr++; 150 } 151 152 sum1=0; ll=1; rr=1; 153 while (rr<=2*brtot-1) 154 { 155 sum1+=ek[rr]; 156 while (sum1>2*l) 157 { 158 sum1-=ek[ll]; ll++; 159 } 160 pre[rr][1]=ll; 161 rr++; 162 } 163 164 dp[0][0]=0; 165 for (i=1;i<=2*brtot-1;i++) 166 { 167 sum1=sumf[i]-sumf[pre[i][0]-1]; 168 sum2=sumk[i]-sumk[pre[i][0]-1]; 169 if ((pre[i][0]-1)%2==1) sum1+=l-sum2; 170 dp[i][1]=sum1; 171 dp[i][0]=max(dp[i-1][0],dp[i][1]); 172 } 173 174 dp[0][2]=0; 175 for (i=1;i<=2*brtot-1;i++) 176 { 177 sum1=sumf[i]-sumf[pre[i][1]-1]; 178 sum2=sumk[i]-sumk[pre[i][1]-1]; 179 if ((pre[i][1]-1)%2==1) sum1+=l-sum2; 180 dp[i][2]=max(dp[i-1][2],sum1); 181 dp[i][2]=max(dp[i][2],dp[i][1]+dp[pre[i][0]-2][0]); 182 } 183 cout<<sumf[2*brtot-1]-dp[2*brtot-1][2]<<endl; 184 } 185 186 int main() 187 { 188 cin>>t; 189 for (k=1;k<=t;k++) 190 { 191 cin>>n>>m>>st>>en>>l; 192 for (i=0;i<=n-1;i++) hd[i]=-1; 193 tot=0; 194 memset(inp,0,sizeof(inp)); 195 memset(outp,0,sizeof(outp)); 196 for (i=1;i<=m;i++) 197 { 198 cin>>x>>y>>z; 199 ins(x,y,z); 200 } 201 202 memset(fl,0,sizeof(fl)); 203 memset(enfl,0,sizeof(enfl)); 204 enfl[en]=1; 205 dfs(st); 206 if (enfl[st]==0) {cout<<-1<<endl; continue;} 207 for (i=0;i<=n-1;i++) 208 if (enfl[i]==0) 209 { 210 j=hd[i]; 211 while (j!=-1) 212 { 213 outp[i]--; inp[e[j].to]--; 214 j=e[j].next; 215 } 216 } 217 218 tuopu(); 219 220 findbridge(); 221 222 countt(); 223 } 224 }