潍坊一中 2023 秋提高级友好学校赛前联测 1 T3

发布时间 2023-10-13 20:33:28作者: FOX_konata

Mystia 的居酒屋

题目背景

小麻雀 Mystia 开了一间居酒屋,每天清晨她都要跨过门前的河流去收集食材。

题目描述

Mystia 想搭一座跨过河的桥,来方便她取得食材。

河是一条无限长的宽度为 \(W\) 的直线,所有在坐标系中符合 \(0≤y≤W\) 的点都属于这条河流。

河面上有 \(N\) 个木桩,附近的杂货店可以买到 \(M\) 种可以使用的木头圆盘,第 \(k\) 个木桩的坐标为 \((X_k,Y_k)\)。第 \(k\) 种圆盘半径为 \(R_k\),每一块的价格为 \(C_k\)

她可以买任意种任意多的圆盘,把它们放到河面上。每一个圆盘的中心都必须为某一个木桩的位置。而且,某些圆盘的一部分可以在地面上,即 \(y<0\)\(y>W\)

Mystia 只能在河两岸或圆盘上移动,两岸即直线 \(y=0\) 或直线 \(y=W\),也可以从一个位置移动到另一个与其相切或相交的圆盘。

请问她修建一座可以从直线 \(y=0\) 到直线 \(y=W\) 走过去的圆盘桥的最小花费是多少?

输入格式

第一行一个整数 \(T\) ,代表测试数据的组数。

对于每组数据:
第一行有三个空格隔开的正整数 \(N,M,W\)
接下来 \(N\) 行,每行两个空格隔开的自然数 \(X_k,Y_k\)
接下来 \(M\) 行,每行两个空格隔开的正整数 \(R_k,C_k\)

输出格式

对于每组数据,输出从直线 \(y=0\) 到直线 \(y=W\) 修建一座可以走过去的桥最少的花费。如果这是不可能完成的,那么输出 impossib1e

样例 #1

样例输入 #1

4
1 1 10
1 5
5 5
11 4 13
19 10
8 7
11 4
26 1
4 2
15 4
19 4
1 9
4 6
19 5
15 10
2 1
3 100
4 10000
5 1000000
11 4 13
19 10
8 7
11 4
26 1
4 2
15 4
19 4
1 9
4 6
19 5
15 10
2 1
3 2
4 3
5 4
1 1 1000000
0 500000
1 1

样例输出 #1

5
206
5
impossib1e

提示

样例解释

对于样例中的第一组数据,如图放置圆盘,刚好可以从河边到达对岸。

对于样例中的第二、三组数据,如图所示,可以修建一座桥。两组数据仅花费不同。

对于样例中的第四组数据,显然无法修建符合题意的桥。

数据范围

每个测试点的具体限制见下表:

测试点编号 \(T≤\) \(N≤\) \(M≤\) \(W,X_k≤\) \(R_k≤\) \(C_k≤\) 特殊性质
\(1\sim2\) \(1\) \(1\) \(1\) \(10^3\) \(10^3\) \(10^3\)
\(3\sim5\) \(10\) \(7\) \(7\) \(10^4\) \(10^4\) \(10^4\)
\(6\sim8\) \(10\) \(40\) \(40\) \(10^4\) \(10^4\) \(10^4\)
\(9\sim12\) \(10\) \(40\) \(40\) \(10^4\) \(10^4\) \(10^4\)
\(13\sim20\) \(10\) \(100\) \(100\) \(10^6\) \(10^6\) \(10^6\)
\(21\sim25\) \(10\) \(100\) \(20100\) \(10^6\) \(200\) \(200\)

特殊性质:对于每组测试数据,所有的 \(X_k\) 均相同。

所有数据保证全部木桩均在河流中,即 \(0≤Y_k≤W\)

先说一下比较朴素的做法,前 20 个点显然分层图跑最短路,发现 21 ~ 25 \(R_k,C_k \leq 200\) ,我们可以把又小又贵的盘子去掉,然后盘子数量就 \(\leq 200\) 直接跑即可,这么做点数 \(O(nm)\) ,边数 \(O(n^2m^2)\) ,但我隐式建图赛时竟然过掉了,其实这么做复杂度是不太对的

考虑优化一下,我们发现复杂度瓶颈在于边数过大,考虑优化建图。我们可以先连一条默认可以使用到的最小盘子,然后如果想换盘子可以在分层图上下两层同一节点上连一条长为 \(c_x - c_y\) 的边来做到换盘子,边数就变成了 \(O(n^2m)\) 的了,直接跑 dijkstra 就可以过

但这个题有 \(O(n^2m)\) 做法。考虑不拆点设 \(f_{i,j}\)\(i\) 上盘子种类为 \(j\) 情况下 \(1 \rightarrow i\) 的距离,那么考虑模拟dijkstra 的过程。记 \(g_i = \min_j f_{i,j}\) ,原问题改为每次选 \(g_i\) 最小的点用它的所有 \(f_{i,j}\) 更新其他所有的 \(f_{k,l}\) ,更新的时候直接双指针可以做到 \(O(m)\) ,做法即枚举转移到 \(v\) 点时使用的盘子 \(r\) ,则 \(u\) 点使用能使用的最小的盘子 \(l\) 。但是这个最短路看起来是不对的,考虑证明,如果松弛顺序有问题只可能因为两个桩子 \(i,j\) ,当前 \(g_i < g_j\) ,且 \(i,j\) 都在最短路上,且 \(i\)\(j\) 靠后;但此时绕道走 \(j\) 唯一目的只能让 \(i\) 换一个更大的盘子,那么如果其盘子由 \(x\) 换到 \(y\) ,直接更改 \(i\) 的盘子只有 \(f_{i,x} - c_x + c_y\) 。而过去至少要更新到 \(g_j + c_x\) 。于是此时一定不会让 \(j\) 去松弛 \(i\) ,更新顺序就是对的了

听起来好像还是有点难以理解,放一下别人的代码:

    int ord[N];
    for(int _=1;_<=n;_++){
        int u=1;
        for(int i=1;i<=n;i++){
            if(vis[u]||!vis[i]&&f[i][0]<f[u][0])u=i;
        }
        ord[u]=_;
        vis[u]=true;
        for(int i=1;i<=n;i++){
            ll d=pow2(ps[i].y-ps[u].y)+pow2(ps[i].x-ps[u].x);
            int l=m,v=f[u][m],mv=inf;
            for(int j=1;j<=m;j++){
                while(l>=2&&pow2(cs[j].r+cs[l-1].r)>=d)l--,v=min(v,f[u][l]);
                if(pow2(cs[j].r+cs[l].r)<d)continue;
                f[i][j]=min(f[i][j],cs[j].c+v);
                f[i][j]=min(f[i][j],mv+cs[j].c);
                f[i][0]=min(f[i][0],f[i][j]);
                mv=min(mv,f[i][j]-cs[j].c); // 这里是为了计算使用更大的盘子的情况
            }
        }
    }