test
好——把问题明确化后我们可以把它归结为一个 加权区间重叠最大值(weighted interval stabbing 的问题:
- 每个正方形(或矩形,边平行坐标轴)记为 \((x_i,y_i,L_i,w_i)\),其中 \(w_i\) 是“该方块轮廓的权值”。
- 黑线是形如 \(y=x+c\) 的所有平移(由参数 \(c\) 决定)。
- 当且仅当存在一点 \((x,y)\) 满足
 \( x\in[x_i,x_i+L_i],\quad y\in[y_i,y_i+L_i],\quad y=x+c \)
 时,黑线与该方块轮廓相交(也即该方块被“命中”)。
- 把上面不等式消去 \(x,y\) 后,可以得到关于 \(c\) 的区间:
 对第 \(i\) 个方块,黑线命中它当且仅当
 \( c \in [,a_i,; b_i,],\quad \text{其中 } a_i = y_i - x_i - L_i,; b_i = y_i + L_i - x_i. \)
 (推导:\(c=y-x\),取 \(y\) 最小、\(x\) 最大得到最小值;取 \(y\) 最大、\(x\) 最小得到最大值。)
因此问题变为:
给定 \(n\) 个实数区间 \([a_i,b_i]\),每个区间有权重 \(w_i\),求某个实数 \(c\) 使得覆盖该点 \(c\) 的区间权重和最大 —— 即求
(\(\max_c \sum_{i: c\in[a_i,b_i]} w_i\))。
这就是经典的扫描/事件点法可以在 \(O(n\log n)\) 或 \(O(n)\)(排序 dominates)内解决的问题。
算法(思路)
- 
对每个方块计算区间端点 (a_i,b_i)。 
- 
构造两个事件: - 在坐标 (a_i) 处发生“加上 (w_i)”;
- 在坐标 (b_i) 处发生“减去 (w_i)”(为了使闭区间计数正确,事件在相同坐标时应先处理所有加事件,再处理减事件,这样当某些区间的右端等于其他区间的左端时,这一点会被两者同时计算到)。
 
- 
对事件按坐标排序,坐标相同时按“加事件先于减事件”排序。 
- 
从左到右扫描事件,维护当前权重和 cur,并在每一步更新答案ans = max(ans, cur)。
- 
最终 ans即为答案;如果还需要对应的c值,可以在更新ans时记录当前坐标(或坐标区间)。
时间复杂度:排序 (O(n\log n)),扫描 (O(n))。内存 (O(n))。
伪代码
events = []  // 每个元素 (coord, type, weight)
for i in 1..n:
    a = y[i] - x[i] - L[i]
    b = y[i] + L[i] - x[i]
    events.append((a, +1, w[i]))   // +1 表示加权重
    events.append((b, -1, w[i]))   // -1 表示减权重
// 排序:先按 coord 升序;若 coord 相同,type = +1(加)排在 type = -1(减)前
sort(events, key = (coord, -type))
cur = 0
ans = 0
best_c = None
for ev in events:
    coord, type, w = ev
    if type == +1:
        cur += w
        if cur > ans:
            ans = cur
            best_c = coord
    else:
        // 在这里减去权值。注意若想把 b 点也视为被包含,则应先处理加事件再处理减事件(我们已经按排序保证)
        cur -= w
return ans, best_c
说明:
best_c记录的是事件坐标(若有一段区间而最大值在区间内部,则任何该段内点都可)。如果你想返回一个具体的c(实数)且最大值在某个开区间内,返回该开区间任一点即可(例如事件坐标之后的一个小邻域内)。
C++ 实现(示例)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct Event {
    long long coord;
    int type; // +1 add, -1 remove
    long long w;
};
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    if(!(cin >> n)) return 0;
    vector<Event> ev;
    for(int i=0;i<n;i++){
        long long x,y,L,w;
        cin >> x >> y >> L >> w;
        long long a = y - x - L;
        long long b = y + L - x;
        ev.push_back({a, +1, w});
        ev.push_back({b, -1, w});
    }
    sort(ev.begin(), ev.end(), [](const Event& A, const Event& B){
        if(A.coord != B.coord) return A.coord < B.coord;
        return A.type > B.type; // +1 (add) should come before -1 (remove)
    });
    long long cur = 0, ans = LLONG_MIN;
    long long bestCoord = 0;
    for(auto &e : ev){
        if(e.type == +1){
            cur += e.w;
            if(cur > ans){
                ans = cur;
                bestCoord = e.coord;
            }
        } else {
            cur -= e.w;
        }
    }
    cout << ans << "\n";
    // 若需要 bestCoord 输出,取消下行注释:
    // cout << "best c approx: " << bestCoord << "\n";
    return 0;
}
注意事项
- 若输入坐标或边长可能为非整数(实数),用 double并适当处理排序的相等容差(EPS);事件排序规则不变(先加后减)。
- 若 w_i可以为负,也可用同样的方法(但若权值负,答案可能是 0 也可能更小,取决问题定义)。
- 若想保证在区间端点同时被计算(即闭区间包含两端),我们在排序时把加事件放在相同 coord时的前面,这样c=a_i=b_j时两个区间都会被计入。
如果你愿意,我可以:
- 把上面的 C++ 换成 Python(方便调试);
- 或者在你给出具体数据(坐标、边长、权值)时替你跑一个示例并给出最大权值与对应的 \(c\)。
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号