【2023牛客多校6】H traffic (思维 斜率)

题目

https://ac.nowcoder.com/acm/contest/57360/H

思路

连通图,边比点多一,那就是有两个环,每个时刻要做的就是删掉两条边消掉这两个环。两种情况,要么是不共边的两个环,要么是共边的两个环。

  • 不共边的两个环,那么两个环的边集分别选一个最大的删掉。
  • 共边的两个环,就变成三个链,拆两条。三个集合的最大值选更大的两个删掉。

要做的就是计算不同横坐标下一堆直线的最大值。可以用李超树,也可以用单调栈。

注意精度。

代码

#include 
using namespace std;

typedef long long ll;
const ll MAXN = 100005;

struct Edge {
    ll a, b;
    ll v_at(ll x) {return a*x + b;}
};
vector<pair<ll, ll>> graph[MAXN];
vector<Edge> edges;

ll dep[MAXN], e_used[MAXN];
pair<ll, ll> fa[MAXN];    // 
vector<pair<pair<ll, ll>, ll>> back_edges;   // <, eid>, dep[u] > dep[v]
vector<vector<ll>> to_del_eids;
ll ans[MAXN];
vector<ll> tmpans[MAXN];

void dfs(ll x, ll Fa) {
    dep[x] = dep[fa[x].first] + 1;
    for (auto [o, eid]: graph[x]) if (!e_used[eid]) {
        e_used[eid] = 1;
        if (dep[o] == 0) {fa[o] = {x, eid}; dfs(o, x);}
        else back_edges.push_back({{x, o}, eid});
    }
}


void calc_to_del_eids() {
    vector<ll> s(edges.size());
    bool flag_intersect = false;
    for (ll i=0; i<2; i++) {
        auto [u, v] = back_edges[i].first;
        s[back_edges[i].second] += (i+1);
        for (ll x=u; x!=v; x=fa[x].first) {
            s[fa[x].second] += (i+1);
            flag_intersect |= (s[fa[x].second] == 3);
        }
    }
    
    to_del_eids.resize(flag_intersect+2);
    for (ll i=0; i<s.size(); i++) if (s[i])
        to_del_eids[s[i]-1].push_back(i);
}





struct Frac {
    __int128 u, v;
    Frac(): u(0), v(1) {}
    Frac(ll uu): u(uu), v(1) {}
    Frac(ll uu, ll vv): u(uu), v(vv) {
        ll g=__gcd(u<0 ? -u : u, v<0 ? v : -v);
        if (g>1) u/=g, v/=g;
        if (v < 0) u=-u, v=-v;
    }

    bool operator<(const Frac& x) {return u*x.v < v*x.u;}
    bool operator<=(const Frac& x) {return u*x.v <= v*x.u;}
};



bool cmp(Edge &x, Edge &y) {return x.a!=y.a ? x.a < y.a : x.b > y.b;}

void fun(const vector<ll> &eids, ll T) {
    vector<Edge> es;
    vector<pair<Frac, Edge>> Q;     // 
    for (ll i: eids) es.push_back(edges[i]);
    sort(es.begin(), es.end(), cmp);
    for (auto e: es) {
        while (!Q.empty()) {
            auto [st, te] = Q.back();
            if (te.a == e.a) break;      // te.b >= te.a
            Frac t_intersect(e.b - te.b, te.a - e.a);
            if (t_intersect <= st) {
                Q.pop_back();
            }
            else {
                Q.push_back({t_intersect, e});
                break;
            }
        }
        if (Q.empty()) Q.push_back({0, e});
    }
    Q.push_back({T+10, Edge{-1,-1}});
    
    ll cur = 0;
    for (ll i=0; i<=T; i++) {
        while (Q[cur+1].first <= i) cur++;
        tmpans[i].push_back(Q[cur].second.v_at(i));
    }
}


void solve()
{
    ll n, T;
    ll sb=0, sa=0;
    cin >>n >>T;
    for (ll i=0, u, v, a, b; i<=n; i++) {
        cin >>u >>v >>a >>b;
        edges.push_back({a,b});
        graph[u].push_back({v, edges.size()-1});
        graph[v].push_back({u, edges.size()-1});
        sa+=a;
        sb+=b;
    }
    
    dfs(1, 0);
    calc_to_del_eids();

    ans[0] = sb;
    for (ll i=1; i<=T; i++) ans[i] = ans[i-1] + sa;

    for (const auto &eids: to_del_eids) fun(eids, T);

    for (ll i=0; i<=T; i++) {
        sort(tmpans[i].rbegin(), tmpans[i].rend());
        cout <<(ans[i]-tmpans[i][0]-tmpans[i][1]) <<'\n';
    }
    
}




int main()
{
    solve();
    return 0;
}

你可能感兴趣的:(深度优先,算法)