20200715005924
给定一个区间 [ 1 , n ] ( n ≥ 2 ) [1,n](n\geq 2) [1,n](n≥2),每次操作可将其下界或上界增加或减少 1 1 1,但长度至少为 1 1 1、下界最小为 1 1 1、上界最大为 n n n,即,若当前区间为 [ L , R ] [L,R] [L,R],则一次操作后可变为以下其中一种:
然而,我们希望区间的长度永远不要降到 1 1 1,于是要把某些操作给禁用掉。
可禁用的操作有 m m m 种,每种可描述为四元组 ( l , r , d i r , c ) (l,r,dir,c) (l,r,dir,c),表示花费 c c c 的代价,可以禁用:
我们可从这 m m m 种中选取任意数量,并付出其代价之和。
求,总共至少要付出多大代价,才能实现目标(区间的长度永远不要降到 1 1 1)?若不可能实现目标则输出 − 1 -1 −1。
2 ≤ n ≤ 500 ; 0 ≤ m ≤ n ( n − 1 ) ; 2\leq n\leq 500; 0\leq m\leq n(n-1); 2≤n≤500;0≤m≤n(n−1);
1 ≤ l < r ≤ n ; d i r ∈ { L , R } ; 1 ≤ c ≤ 1 0 6 ; 1\leq l
例1(我对拍产生的):
4 10
2 3 R 18
3 4 L 7
2 3 L 69
1 2 L 45
1 3 L 90
1 2 R 69
1 4 L 47
1 3 R 97
2 4 R 41
3 4 R 41
如果把每种状态 [ L , R ] [L,R] [L,R] 视为无向图中的一个节点,则一对互相变化的操作就对应一条无向边(即 [ L , R ] [L,R] [L,R] 与 [ L − 1 , R ] , [ L + 1 , R ] , [ L , R − 1 ] , [ L , R + 1 ] [L-1,R],[L+1,R],[L,R-1],[L,R+1] [L−1,R],[L+1,R],[L,R−1],[L,R+1] 连边),边权为此操作被禁用的代价(若不可被禁用则设为 + ∞ +\infin +∞)。那么,我们就是要取一部分边,使得去除这些边之后 [ 1 , n ] [1,n] [1,n] 对应点分别与 ∀ i , [ i , i ] \forall i, [i,i] ∀i,[i,i] 对应点不连通,且其边权之和最小(不可能实现目标当且仅当边权之和为 + ∞ +\infin +∞)。
例1的示意图:黑点和黑边是我们建的图。
再增设一节点 T T T,并分别与 ∀ i , [ i , i ] \forall i, [i,i] ∀i,[i,i] 连边(边权为 + ∞ +\infin +∞),所有边以其边权为容量,那么问题就转化为以 [ 1 , n ] [1,n] [1,n] 对应点为源点、以 T T T 为汇点的最小割问题,其中,不可能实现目标当且仅当最小割为 + ∞ +\infin +∞。
【WA】最小割等于最大流。值得注意的是,若点集 V V V 被分为 V 1 , V 2 V_1,V_2 V1,V2,其中 源 点 ∈ V 1 ; 汇 点 ∈ V 2 ; V 1 ∩ V 2 = ϕ ; V 1 ∪ V 2 = V 源点\in V_1; 汇点\in V_2; V_1\cap V_2=\phi; V_1\cup V_2=V 源点∈V1;汇点∈V2;V1∩V2=ϕ;V1∪V2=V,那么其割值是 V 1 V_1 V1 所有出边的容量(权值)之和,而不考虑入边。因为,割的含义可理解为这一条“分界线”对最大流的限制,而点集 V V V 有各种各样的割法、各有各的限制,其中割值最小的割法就成为全图的瓶颈之处,因此最小割等于最大流。既然不考虑入边,而我们的图是无向图,因此每条边实际建图时都应建双向边。此题使我对最小割的理解进一步深入。
以例1示意图为例,若只建向下、向右方向有初始容量的边,则最小割为97+18+69+47,略过了 [ 2 , 3 ] ↔ [ 2 , 4 ] [2,3]\leftrightarrow[2,4] [2,3]↔[2,4] 之间的容量为 41 41 41 的边。
【WA】另外,对 + ∞ +\infin +∞ 的处理也有讲究:
由上述要求可确定两个 + ∞ +\infin +∞ 的实际取值。
【TLE】但是最小割的思路会超时。下面介绍最短路的做法。
去除上图中的黑点黑边,得:
我们会发现,我们需要选择一部分绿边形成分界线,把左上角的 [ 1 , n ] [1,n] [1,n] 块与右下角的 ∀ i , [ i , i ] \forall i, [i,i] ∀i,[i,i] 块分隔开。
为了使选中绿边的权值之和最小,我们选中的绿边所生成的图(分界线)不仅是一棵树(图上有环是没必要的),而且还是一条链(树上多余的分支也是没必要的)。
这条链的一端应该与整幅图的“最左端”相连、另一端与整幅图的“最上端”相连。如果我们设立源点、把最左端各水平边的左端点连向源点,设立汇点、把最上端各竖直边的上端点连向汇点,则问题又转化为了最短路问题!其中,红边的权值设为 + ∞ +\infin +∞、与源点或汇点相连的边的权值设为 0 0 0。最短路等于 + ∞ +\infin +∞ 当且仅当不可能实现目标。
【RE】某些数组开小了。
【RE】memset(vis , false , sizeof ans)
,半天没找出来。
【WA】忘了超过 + ∞ +\infin +∞ 应输出 − 1 -1 −1。
说明: [ L , R ] [L,R] [L,R] 对应块的左下角坐标为 [ L − 1 , R − 1 ] [L-1,R-1] [L−1,R−1]、右上角坐标为 [ L , R ] [L,R] [L,R],依此类推。
#include
#include
#include
#define ll long long
char str[10];
ll cost0[505][505][2];
const int fu=0, fv=1, fnext=2;
int edge[600000][3]; // *2
ll cost[600000]; // *2
int last[300000];
int edges=0;
void add_edge(int u , int v , ll c){
edge[edges][fu] = u;
edge[edges][fv] = v;
edge[edges][fnext] = last[u];
last[u] = edges;
cost[edges] = c;
++edges;
}
void add_edges(int u , int v , ll c){
add_edge(u,v,c);
add_edge(v,u,c);
}
// dijkstra
struct Node{
int v;
ll dis;
Node(int _v=0 , ll _dis=0):v(_v),dis(_dis){}
bool operator < (const Node &n2) const{ return dis > n2.dis; }
};
std::priority_queue<Node> que;
bool vis[300000];
ll ans[300000];
int main(){
int n,m;
scanf("%d%d" , &n , &m);
for(int L=1 ; L<=n ; ++L) for(int R=L+1 ; R<=n ; ++R) cost0[L][R][0]=cost0[L][R][1]=1e12;
//printf("cost[1][2][0]=%lld\n" , cost[1][2][0]);
for(int i=0 ; i<m ; ++i){
int L,R,c;
scanf("%d%d%s%d" , &L , &R , str , &c);
cost0[L][R][ ((str[0]=='L')?1:0) ] = c;
}
memset(last , -1 , sizeof last);
edges = 0;
for(int L=1 ; L<=n ; ++L){
for(int R=L+1 ; R<=n ; ++R){
add_edges( (L-1)*501+(R-1) , L*501+(R-1) , cost0[L][R][0] );
add_edges( L*501+R , L*501+(R-1) , cost0[L][R][1] );
}
}
int S = 501*501; for(int L=1 ; L<n ; ++L) add_edges( L*501+n , S , 0 );
int T = 501*501+1; for(int R=1 ; R<n ; ++R) add_edges( R , T , 0 );
memset(vis , false , sizeof vis); // not sizeof ans
memset(ans , 0x3f , sizeof ans);
//while(! que.empty()) que.pop();
ans[S]=0;
que.push(Node(S,ans[S]));
while(! que.empty()){
int u = que.top().v; que.pop();
if(vis[u]) continue;
vis[u] = true;
for(int e=last[u] ; e>=0 ; e=edge[e][fnext]){
int v = edge[e][fv];
if( vis[v]==false && ans[v]>ans[u]+cost[e]){
ans[v] = ans[u]+cost[e];
que.push(Node(v,ans[v]));
}
}
}
if(ans[T]>=1e12) ans[T]=-1; // forgot
printf("%lld\n" , ans[T]);
return 0;
}
StdDraw.java
)import java.util.Scanner;
public class A20200713_nc02_I {
public static int ran(int L , int R) {
return (int)(Math.random()*(R-L+1))+L;
}
public static void main(String[] args) {
Scanner mi = new Scanner(System.in);
int n = mi.nextInt();
int m = mi.nextInt();
StdDraw.setScale(-2 , n+1);
StdDraw.setPenRadius(0.01);
StdDraw.setPenColor(StdDraw.PINK);
for(int L=1 ; L<=n ; ++L) StdDraw.line(L , n , n/2 , n+0.5);
for(int R=1 ; R<=n ; ++R) StdDraw.line(-1.5 , n/2 , 0 , R-1);
StdDraw.setPenColor(StdDraw.BLACK);
for(int L=1 ; L<=n ; ++L) StdDraw.text(L-0.5 , -0.5 , "L="+L);
for(int R=1 ; R<=n ; ++R) StdDraw.text(-0.5 , R-0.5 , "R="+R);
/*StdDraw.setPenRadius(0.004);
for(int L=1 ; L<=n ; ++L) for(int R=L ; R<=n ; ++R) {
StdDraw.filledCircle(L-0.5 , R-0.5 , 0.05);
if(L
StdDraw.setPenRadius(0.01);
StdDraw.setPenColor(StdDraw.RED);
for(int L=1 ; L<=n ; ++L) {
for(int R=L ; R<=n ; ++R) {
StdDraw.line(L-1 , R-1 , L , R-1);
StdDraw.line(L , R-1 , L , R);
//StdDraw.text(L-0.5 , R-0.5 , L*501+R+"");
}
}
for(int i=0 ; i<m ; ++i) {
int L = mi.nextInt();
int R = mi.nextInt();
char dir = mi.next().charAt(0);
int c = mi.nextInt();
if(dir=='L') {
StdDraw.setPenColor(StdDraw.GREEN);
StdDraw.line(L , R-1 , L , R);
StdDraw.setPenColor(StdDraw.BLUE);
StdDraw.text(L , R-0.5 , c+"");
}else {
StdDraw.setPenColor(StdDraw.GREEN);
StdDraw.line(L-1 , R-1 , L , R-1);
StdDraw.setPenColor(StdDraw.BLUE);
StdDraw.text(L-0.5 , R-1 , c+"");
}
//System.out.println("Draw L="+L+" , R="+R+" , dir="+dir+" , c="+c+".");
}
/*StdDraw.setPenColor(StdDraw.PINK);
while(true) {
if(! StdDraw.isMousePressed())continue;
StdDraw.point(StdDraw.mouseX() , StdDraw.mouseY());
}*/
}
}