luogu P3357 最长k可重线段集问题

这题和3358一模一样,建模形式直接不用变,就两点不一样,一是len变化了,加入y后再更新即可,还有就是可能会出现x0=x1的情况,即一条开线段垂直x轴,如果我们依旧按照上一题的建图方法,就会出现负权环,无法跑出答案,我们就可以把一个点拆成入点和出点,这样无论是否是不是垂直都可以一样建,注意开long long,不开long long可能只有9分

#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) ((x)&(-x))
#define sqr(x) ((x)*(x))
typedef long long LL;

const int maxm = 1e5+5;
const LL INF = 0x3f3f3f3f3f3f3f3f;

struct edge{
    LL u, v, cap, flow, cost, nex;
} edges[maxm];

struct Points{
    LL l, r, len;
} point[505];

LL head[maxm], cur[maxm], cnt, fa[1024<<1], n, d[1024<<1], allx[1024];
bool inq[1024<<1];

void init() {
    memset(head, -1, sizeof(head));
}

void add(int u, int v, LL cap, LL cost) {
    edges[cnt] = edge{u, v, cap, 0, cost, head[u]};
    head[u] = cnt++;
}

void addedge(int u, int v, LL cap, LL cost) {
    add(u, v, cap, cost), add(v, u, 0, -cost);
}

bool spfa(int s, int t, int &flow, LL &cost) {
    for(int i = 0; i <= n+2; ++i) d[i] = INF; //init()
    memset(inq, false, sizeof(inq));
    d[s] = 0, inq[s] = true;
    fa[s] = -1, cur[s] = INF;
    queue<int> q;
    q.push(s);
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        inq[u] = false;
        for(int i = head[u]; i != -1; i = edges[i].nex) {
            edge& now = edges[i];
            int v = now.v;
            if(now.cap > now.flow && d[v] > d[u] + now.cost) {
                d[v] = d[u] + now.cost;
                fa[v] = i;
                cur[v] = min(cur[u], now.cap - now.flow);
                if(!inq[v]) {q.push(v); inq[v] = true;}
            }
        }
    }
    if(d[t] == INF) return false;
    flow += cur[t];
    cost += 1LL*d[t]*cur[t];
    for(int u = t; u != s; u = edges[fa[u]].u) {
        edges[fa[u]].flow += cur[t];
        edges[fa[u]^1].flow -= cur[t];
    }
    return true;
}

int MincostMaxflow(int s, int t, LL &cost) {
    cost = 0;
    int flow = 0;
    while(spfa(s, t, flow, cost));
    return flow;
}

void run_case() {
    init();
    LL l, r, y1, y2;
    int k, xcnt = 0;
    cin >> n >> k;
    for(int i = 1; i <= n; ++i) {
        cin >> l >> y1 >> r >> y2;
        LL tmp = 1LL*floor(sqrt(sqr(r-l)+sqr(y2-y1)));
        if(l > r) swap(l, r);
        l <<= 1, r <<= 1;
        if(l == r) r|=1; else l|=1;
        allx[++xcnt] = l, allx[++xcnt] = r, point[i] = Points{l, r, tmp};
    }
    sort(allx+1,allx+1+xcnt);
    int len = unique(allx+1,allx+1+xcnt)-allx;
    for(int i = 1; i <= n; ++i) {
        point[i].l = lower_bound(allx+1,allx+len,point[i].l)-allx;
        point[i].r = lower_bound(allx+1,allx+len,point[i].r)-allx;
    }
    for(int i = 1; i < len-1; ++i)
        addedge(i, i+1, INF, 0);
    int s = 0, t = len;
    for(int i = 1; i <= n; ++i) {
        addedge(point[i].l, point[i].r, 1, -point[i].len);
    }
    addedge(s, 1, k, 0), addedge(len-1, t, k, 0);
    LL cost = 0;
    n = len;
    MincostMaxflow(s, t, cost);
    cout << -cost;
}

int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    run_case();
    cout.flush();
    return 0;
}

原文地址:https://www.cnblogs.com/GRedComeT/p/12288375.html

时间: 2024-11-09 02:46:05

luogu P3357 最长k可重线段集问题的相关文章

洛谷 P3357 最长k可重线段集问题【最大流】

pre:http://www.cnblogs.com/lokiii/p/8435499.html 和最长k可重区间集问题差不多,也就是价值的计算方法不一样,但是注意这里可能会有x0==x1的情况也就是l==r的情况,然后就TTTTTLE. 其实处理方法很粗暴,因为是开线段,所以可以把它扩大一倍,然后就可以取精度差,对于l!=r,l++,否则l--. 然后正常建模即可. 这个建模大概是用了取补集的思想,把覆盖和没覆盖相转化. #include<iostream> #include<cstd

最长k可重线段集问题

最长k可重线段集问题 时空限制1000ms / 128MB 题目描述 给定平面 x−O−y 上 n 个开线段组成的集合 I,和一个正整数 k .试设计一个算法,从开线段集合 I 中选取出开线段集合 S⊆I ,使得在 x 轴上的任何一点 p,S 中与直线 x=p 相交的开线段个数不超过 k,且∑?∣z∣达到最大.这样的集合 S 称为开线段集合 I 的最长 k 可重线段集.∑?∣z∣ 称为最长 k 可重线段集的长度. 对于任何开线段 z,设其断点坐标为 (x0?,y0?) 和 (x1?,y1?),则

*LOJ#6227. 「网络流 24 题」最长k可重线段集问题

$n \leq 500$条平面上的线段,问一种挑选方法,使得不存在直线$x=p$与挑选的直线有超过$k$个交点,且选得的直线总长度最长. 横坐标每个点开一个点,一条线段就把对应横坐标连一条容量一费用(-长度)的边:点$x$向点$x+1$连一条容量$k$费用0的边.这里的$k$边限制的是直线上其他不经过这里的地方. 这里有个trick就是有与$x$轴垂直的线段.直接判掉会wa.为此把坐标扩大两倍,如果$l=r$那么$r++$否则$l++$,相当于把一个点拆成两个. 原文地址:https://www

网络流24题之最长k可重线段集问题

对于每个线段拆成两个点,如同之前一样建图,由于可能出现垂直于x轴的 所以建图由i指向i~ 继续最小费用最大流 By:大奕哥 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=10000005,inf=1e9; 4 int head[N],d[N],f[N],l1[N],r1[N],l2[N],r2[N],a[N],s=1e9,t,n,k,cnt=-1; 5 long long cost; 6 bool v[N]; 7

最长k可重区间集问题

最长k可重区间集问题 题目链接 https://www.luogu.org/problemnew/show/3358 做法 所有点向下一个点连容量为k费用为0的边 l和r连容量为1费用为区间长度的边 然后跑最大流最大费用流 (最大费用就是把边权取相反数跑最小费用 最后再输出最终费用的相反数) 思考 在整张图中,只有l - >r的边有费用 而且费用为区间长度 (i->i+1费用为0) 所以跑最大费用也就是求最长区间 #include <algorithm> #include <

最长k可重区间集(cogs 743)

?问题描述:?编程任务:对于给定的开区间集合I和正整数k,计算开区间集合I的最长k可重区间集的长度.?数据输入:由文件interv.in提供输入数据.文件的第1 行有2 个正整数n和k,分别表示开区间的个数和开区间的可重迭数.接下来的n行,每行有2个整数,表示开区间的两个端点坐标.?结果输出:程序运行结束时,将计算出的最长k可重区间集的长度输出到文件interv.out中.输入文件示例 输出文件示例interv.in4 21 76 87 10 9 13 interv.out 15 /* 朴素的做

[网络流 24 题]最长k可重区间集(费用流)

Description 给定实直线L 上n 个开区间组成的集合I,和一个正整数k,试设计一个算法,从开区间集合I 中选取出开区间集合S属于I,使得在实直线L 的任何一点x,S 中包含点x 的开区间个数不超过k,且sum(|z|)z属于S,达到最大.这样的集合S称为开区间集合I的最长k可重区间集.sum(|z|) z属于S称为最长k可重区间集的长度.对于给定的开区间集合I和正整数k,计算开区间集合I的最长k可重区间集的长度. Solution 1.离散化 然后从每个点i向i+1连一条流量为INF,

【网络流24题】No.21 (最长 k 可重区间集问题 最长不相交路径 最大费用流)

[] 输入文件示例input.txt4 21 76 87 109 13 输出文件示例output.txt15 [分析] 直接co题解好了,写得挺全.. [建模方法] 方法1 按左端点排序所有区间,把每个区间拆分看做两个顶点<i.a><i.b>,建立附加源S汇T,以及附加顶点S'. 1.连接S到S'一条容量为K,费用为0的有向边.2.从S'到每个<i.a>连接一条容量为1,费用为0的有向边.3.从每个<i.b>到T连接一条容量为1,费用为0的有向边.4.从每个

「网络流24题」「LuoguP3358」 最长k可重区间集问题

题目描述 对于给定的开区间集合 I 和正整数 k,计算开区间集合 I 的最长 k可重区间集的长度. 输入输出格式 输入格式: 的第 1 行有 2 个正整数 n和 k,分别表示开区间的个数和开区间的可重迭数.接下来的 n行,每行有 2 个整数,表示开区间的左右端点坐标. 输出格式: 将计算出的最长 k可重区间集的长度输出 输入输出样例 输入样例#1: 复制 4 2 1 7 6 8 7 10 9 13 输出样例#1: 复制 15 说明 对于100%的数据,1<=n<=500,1<=k<