【题目描述】
如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段。
第二段:本题改编自Usaco Training 4.4.2...
第三段:本题加大了数据强度...
第四段:本题来自CH Round #1...
第五段:快去看第六段!
Tangent来到OI村,想起Bread经常在他面前晒妹(Lemon),于是要把二人分隔两地,永世不能相见。
黑化的Tangent拥有了分裂大地的力量,他要分裂两人的家之间的一些路,使得Bread不能去找Lemon。(保证Bread家和Lemon家连通)
从Bread家到Lemon家的路现在可以看成是一个有向图,具有N个点,M条边。(点的编号为1~N,边的编号按照离Tangent的距离由近到远依次为1~M)
Tangent想要毁坏一条边的代价是Wi。
由于Tangent想要节省力量去毁坏更多和谐的事物,所以他的炸路方案必定是总代价最小、边数量最小的,而且他希望能尽快做完这件事,所以他炸的路对应编号必定是字典序最小的。
【输入描述】
第一行四个正整数N,M,S0,T0,分别表示点数,边数,Bread家的点编号,Lemon家的点编号。
接下来N行,按照边的编号依次描述每条边,每行三个正整数Si,Ti,Wi,分别表示第i条边的起点、终点和毁坏代价。
【输出描述】
第一行两个正整数W和K,表示总最小代价和最小炸路数量。
接下来K行,输出最小字典序方案,每行一个正整数Number,表示第Number条边要炸毁。
【样例输入】
4 5 1 4
1 3 100
3 2 50
2 4 60
1 2 40
2 3 80
【样例输出】
60 1
3
【解题思路】
题目是一道裸的最小割(话说为何USACO的题都特别裸……)有点不同的是它不仅要求你总代价最小,还要边数最少,还要输出炸了哪些路。
首先我们来看怎么让总代价最小的同时让边数最小。
求边数最小应该还是很容易想到每炸一条边就把计数器+1吧?但显然在求网络流的过程中我们没法知道它删没删边,走得哪条边。于是我们可以用一个巧妙的办法:将每条边的边权*(m+1)+1。
为什么是*(m+1)呢?
不难发现,即使每条边都要炸,也最多只加了m,于是我们将最后的答案div (m+1)就得到总代价了,同理mod (m+1)就得到炸的边数了。
然后对于炸的是哪些路径,我们可以用类似于最短路找所经过的路径的办法(或者求次短路的办法),枚举每条边将其删去,然后再跑最大流,如果所得结果加上该边边权与之前答案相同,那么该边就是被炸的边了。为了加速程序运行,我们可以把这条边删去,同时答案减去该边权。
顺便再讲一个优化……(用dinic做这题不加这优化好像过不去……?)
我们发现数据范围极其恶心,N=50而M=5000,我们在枚举删边的时候构图会有很大的麻烦,时间复杂度蹭蹭地上去了,然后发现M>N^2,肯定有重边,所以我们可以把重边记录在一起,删边的时候就只要减去当前边边权即可,这样不仅加快了构图速度,还加快了网络流速度。详见代码。最后是一个注意事项:因为每条边的边权*(m+1)+1了,所以很多东西要开long long,无穷大也要变为LLONG_MAX。
【代码实现】
1 #include
2 #include
3 #include
4 #include