Submission #1255753

#TimeUsernameProblemLanguageResultExecution timeMemory
1255753AnhPhamToll (APIO13_toll)C++20
0 / 100
0 ms320 KiB
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll INF = (ll)1e15;

struct Edge{int u,v; ll w; int id; bool isNew;};

int N,M,K;
vector<Edge> oldEdges;
vector<pair<int,int>> newEdges;
vector<ll> people;

struct DSU{vector<int> p; DSU(int n=0){p.resize(n+1); iota(p.begin(), p.end(), 0);} int find(int x){return p[x]==x?x:p[x]=find(p[x]);} bool unite(int a,int b){a=find(a); b=find(b); if(a==b) return false; p[a]=b; return true;}};

// Build MST given mask: chosen new edges have weight 0, unchosen weight INF
// Return whether all chosen edges are in MST, and the MST adjacency list

bool buildMST(int mask, vector<vector<pair<int,ll>>>& g, vector<int>& newEdgeInMST){
    vector<Edge> edges;
    for(auto &e: oldEdges) edges.push_back(e);
    for(int i=0;i<K;i++){
        ll w = (mask>>i & 1)? 0 : INF;
        edges.push_back({newEdges[i].first, newEdges[i].second, w, i, true});
    }
    // sort by weight, tie-break: prefer old edges (isNew false)
    sort(edges.begin(), edges.end(), [](const Edge &a, const Edge &b){ if(a.w!=b.w) return a.w < b.w; return a.isNew < b.isNew; });
    DSU dsu(N);
    g.assign(N+1, {});
    newEdgeInMST.assign(K, 0);
    int cnt=0;
    for(auto &e: edges){
        if(dsu.unite(e.u, e.v)){
            g[e.u].push_back({e.v, e.w});
            g[e.v].push_back({e.u, e.w});
            if(e.isNew) newEdgeInMST[e.id]=1;
            cnt++; if(cnt==N-1) break;
        }
    }
    if(cnt!=N-1) return false;
    // check all chosen present
    for(int i=0;i<K;i++) if(mask>>i &1){ if(!newEdgeInMST[i]) return false; }
    return true;
}

// LCA prep to get max old-edge weight on path between u and v
int LOG;
vector<vector<int>> up;
vector<vector<ll>> upMaxOld; // max old-edge weight on path to ancestor
vector<int> depth;

void dfsPrep(int u,int p, vector<vector<pair<int,ll>>>& g){
    for(auto [v,w]: g[u]) if(v!=p){
        depth[v]=depth[u]+1;
        up[0][v]=u;
        // if edge is from oldEdges, its weight < INF; new edges chosen have weight 0
        // we only want to consider old edges' weights for price cap, so store w if w<INF else 0
        upMaxOld[0][v] = (w>=INF ? 0 : w);
        dfsPrep(v,u,g);
    }
}

ll maxOldOnPath(int u,int v){
    if(depth[u]<depth[v]) swap(u,v);
    ll ans=0;
    int diff = depth[u]-depth[v];
    for(int i=0;i<LOG;i++) if(diff>>i &1){ ans = max(ans, upMaxOld[i][u]); u = up[i][u]; }
    if(u==v) return ans;
    for(int i=LOG-1;i>=0;i--) if(up[i][u]!=up[i][v]){
        ans = max(ans, upMaxOld[i][u]); ans = max(ans, upMaxOld[i][v]);
        u = up[i][u]; v = up[i][v];
    }
    ans = max(ans, upMaxOld[0][u]); ans = max(ans, upMaxOld[0][v]);
    return ans;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin>>N>>M>>K;
    oldEdges.resize(M);
    for(int i=0;i<M;i++){int u,v; ll w; cin>>u>>v>>w; oldEdges[i]={u,v,w,i,false};}
    newEdges.resize(K);
    for(int i=0;i<K;i++) cin>>newEdges[i].first>>newEdges[i].second;
    people.assign(N+1,0);
    for(int i=1;i<=N;i++) cin>>people[i];

    ll best=0;
    int subsets = 1<<K;
    vector<vector<pair<int,ll>>> g; vector<int> newInMST;

    for(int mask=0; mask<subsets; mask++){
        if(!buildMST(mask, g, newInMST)) continue;
        // prepare LCA
        LOG = 1; while((1<<LOG) <= N) LOG++;
        up.assign(LOG, vector<int>(N+1, 0));
        upMaxOld.assign(LOG, vector<ll>(N+1, 0));
        depth.assign(N+1, 0);
        dfsPrep(1,0,g);
        for(int k=1;k<LOG;k++){
            for(int v=1;v<=N;v++){
                up[k][v] = up[k-1][ up[k-1][v] ];
                upMaxOld[k][v] = max(upMaxOld[k-1][v], upMaxOld[k-1][ up[k-1][v] ]);
            }
        }
        // compute subtree sums to get pass counts for edges in MST
        vector<ll> subs(N+1,0);
        function<void(int,int)> dfsSum = [&](int u,int p){
            subs[u]=people[u];
            for(auto [v,w]: g[u]) if(v!=p){ dfsSum(v,u); subs[u]+=subs[v]; }
        };
        dfsSum(1,0);
        ll totalPeople=0; for(int i=1;i<=N;i++) totalPeople+=people[i];
        // For each new edge i that is in MST (newInMST[i]==1 and mask has it chosen), find its price cap
        ll revenue=0;
        // Need to locate the edge in MST corresponding to the new edge: endpoints are newEdges[i]
        for(int i=0;i<K;i++) if( (mask>>i &1) && newInMST[i]){
            int u=newEdges[i].first, v=newEdges[i].second;
            // price cap = max old-edge weight on path u-v in this MST
            ll price = maxOldOnPath(u,v);
            // find passCount: in MST edge corresponding to this new edge, which child side size?
            // We need to find the edge in MST that connects the two parts: that edge may be one where up relation exists.
            // We can determine pass count by walking from deeper node to its parent until reaching LCA, but easier: if we temporarily remove this new edge in MST,
            // the cut corresponds to subtree of one endpoint when treating the MST as rooted at 1 and considering parent relations.
            // We can find LCA and decide which node is deeper when the edge in tree is between parent-child.
            // Let's find LCA
            int a=u, b=v;
            if(depth[a]<depth[b]) swap(a,b);
            // lift a to depth b
            int diff = depth[a]-depth[b];
            for(int t=0;t<LOG;t++) if(diff>>t &1) a = up[t][a];
            while(a!=b){
                int ta=a, tb=b;
                for(int t=LOG-1;t>=0;t--) if(up[t][a]!=up[t][b]){ a=up[t][a]; b=up[t][b]; }
                a = up[0][a]; b = up[0][b];
            }
            int lca = a;
            // Now determine which node is child in tree for the MST-edge corresponding to the new edge.
            // In our MST built, the new edge itself is an edge between endpoints; it appears in adjacency. We need to find which endpoint is deeper when considering parent from dfsPrep.
            int node=-1;
            if(up[0][ newEdges[i].first ] == newEdges[i].second) node = newEdges[i].first;
            else if(up[0][ newEdges[i].second ] == newEdges[i].first) node = newEdges[i].second;
            else {
                // If neither is direct parent of the other (because our root might be elsewhere), we can use depth: the deeper endpoint's subtree when removing edge
                node = (depth[newEdges[i].first] > depth[newEdges[i].second]) ? newEdges[i].first : newEdges[i].second;
            }
            ll side = subs[node];
            ll pass = min(side, totalPeople - side);
            revenue += pass * price;
        }
        best = max(best, revenue);
    }
    cout<<best<<"\n";
    return 0;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...