Submission #916138

#TimeUsernameProblemLanguageResultExecution timeMemory
916138ThylOneTwo Currencies (JOI23_currencies)C++14
100 / 100
1159 ms68960 KiB
//####################
//TwoCurrencies
//####################
#include<bits/stdc++.h>
#include <functional>
    
using ll = long long;
using namespace std;
const int maxiN = 1000042;
const int LOG = 19;
    
int n, m, q;
namespace PathSum{

    vector < int > links[maxiN];
    int depth[maxiN];
    int up[maxiN][LOG];
    void root(int node, int d = 0, int parent = -1) {
        up[node][0] = parent;
        depth[node] = d;
        for (int v: links[node])
            if (v != parent) {
                root(v, d + 1, node);
            }
    }
    int getK(int node, int K) {
        for (int i = LOG - 1; i >= 0; i--) {
            if ((K >> i) & 1) {
                node = up[node][i];
            }
        }
        return node;
    }
    int LCA(int a, int b) {
        if (depth[b] > depth[a]) swap(a, b);
        a = getK(a, depth[a] - depth[b]);
        if (a == b) {
            return a;
        }
        for (int l = LOG - 1; l >= 0; l--) {
            if (up[a][l] != up[b][l]) {
                a = up[a][l];
                b = up[b][l];
            }
        }
        return up[a][0];
    }
        
    pair<int,int> eulerseg[maxiN];
    int actEuleur=-1;
    void computeEuleurTour(int node,int parent = -1){
        actEuleur++;
        eulerseg[node].first = actEuleur;
        for(int v:links[node])if(parent!=v){
            computeEuleurTour(v,node);
        }
        actEuleur++;
        eulerseg[node].second = actEuleur;
    }
    template<typename T>
    struct segtree{
        vector<T> vals;
        int leaf;
        segtree(int n){
            leaf = (1<<((int)ceil(log2(n))));
            vals.resize(2*leaf);
            fill(vals.begin(),vals.end(),0);
        }
        segtree()=default;
        void init(int n){
            leaf = (1<<((int)ceil(log2(n))));
            vals.resize(2*leaf);
            fill(vals.begin(),vals.end(),0);
        }
        void reset(){
            fill(vals.begin(),vals.end(),0);
        }
        void _addRange(int a,int b,T val){
            if(a==b){
                vals[a]+=val;
            }else if(a&1){
                vals[a]+=val;
                _addRange(a+1,b,val);
            }else if(b%2==0){
                vals[b]+=val;
                _addRange(a,b-1,val);
            }else{
                _addRange(a/2,b/2,val);
            }
        }
        void addRange(int a,int b,T val){
            _addRange(a+leaf,b+leaf,val);
        }
        T getVal(int pos){
            pos+=leaf;
            T re = 0;
            for(int p=pos;p>=1;p>>=1){
                re+=vals[p];
            }
            return re;
        }
        T sumPath(int a,int b,int lca){
            if(b==lca)swap(a,b);
            if(a==lca){
                return getVal(eulerseg[b].first) - getVal(eulerseg[lca].first);
            }else{
                return getVal(eulerseg[a].first)+getVal(eulerseg[b].first) - 2*getVal(eulerseg[lca].first);
            }
            
        }
    };
    void treeStuff(){
        root(0, 0, -1);
        computeEuleurTour(0);
        //On calcule la tab de LCA
        for (int l = 1; l < LOG; l++) {
            for (int i = 0; i < n; i++) {
                if (up[i][l - 1] == -1) {
                    up[i][l] = -1;
                }
                up[i][l] = up[up[i][l - 1]][l - 1];
            }
        }
    }

    segtree<ll> pathsum;
    segtree<int> occurence;


    template<typename T>
    void updatePath(segtree<T> &st,int node,int v){
        st.addRange(eulerseg[node].first,eulerseg[node].second,v);
    }
};
using namespace PathSum;

struct req{
    int a,b;
    ll x,y;
    int lca;
    ll totalSum;
};
//On stock (cout péage, node)
vector<pair<ll,int>> W_child;
//les requetes
vector<req> queries;
//La somme totale de gold enlevé et le nb d'or
vector<pair<ll,int>> ans;

pair<ll,int> computePath(int a,int b,int lca){
    int nbocc=0;
    ll sumGold=0;
    nbocc=occurence.sumPath(a,b,lca);
    sumGold=pathsum.sumPath(a,b,lca);
    return {sumGold,nbocc};
}
const bool AU_MOINS_ASSEZ=true;
const bool STRICT_PAS_ASSEZ_ACTIF=false;
bool oracle(int r){
    return queries[r].y>=(queries[r].totalSum-pathsum.sumPath(queries[r].a,queries[r].b,queries[r].lca));
}
void parallel_binary_search(){
    /*
        On dichotomise sur le nombre de piece d'or utilisé
    */ 
    //initialisation des intervals dichotmique
    vector<pair<int,int>> requetes(q);
    for(int i = 0;i<q;i++)
        requetes[i] = {0,m};
    
    for(int lvl=0;lvl<LOG;lvl++){
        //On clean les segtrees
        pathsum.reset();
        occurence.reset();
        //On stock la mid val de chaque segments pour les traiter quand on passe sur cette borne
        vector<int> check[m+1];
        for(int i = 0;i<q;i++){  
            int mid = (requetes[i].first+requetes[i].second)/2;
            check[mid].push_back(i);
        }
        for(int borne=0;borne<=m;borne++){
            //On update les segtrees
            if(borne!=0){
                int node = W_child[borne-1].second;
                ll W = W_child[borne-1].first;
                updatePath(occurence,node,1);
                updatePath(pathsum,node,W);
            }
            for(int r:check[borne]){
                if(requetes[r].first==requetes[r].second){
                    //calculer les donnes finales
                    ans[r] = computePath(queries[r].a, queries[r].b, queries[r].lca);
                }else{
                    bool result = oracle(r);
                    //processus dichotomique
                    if(result==AU_MOINS_ASSEZ){
                        requetes[r] = {requetes[r].first,borne};
                    }else{
                        requetes[r] = {borne+1,requetes[r].second};
                    }
                }
            }
        }
    }
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m >> q;
    queries.resize(q);
    occurence.init(2*n);
    pathsum.init(2*n);
    ans.resize(q);
    fill(depth, depth + maxiN, -1);

    for(int i = 0;i<q;i++)ans[i] = {-1,-1};
    vector < pair < int, int >> edges;
    for (int i = 0; i < n - 1; i++) {
        int a, b;
        cin >> a >> b;
        a--;
        b--;
        edges.push_back({a,b});
        links[a].push_back(b);
        links[b].push_back(a);
    }
    treeStuff();
    
    for (int i = 0; i < m; i++) {
        int p, c;
        cin >> p >> c;
        p--;
        auto a = edges[p].first;
        auto b = edges[p].second;
        if (depth[a] < depth[b]) swap(a, b);  
        updatePath(occurence,a,1);
        updatePath(pathsum,a,c);
        W_child.push_back({c,a});
    }
    sort(W_child.rbegin(),W_child.rend());
    
    for (int i = 0; i < q; i++) {
        int a,b;
        ll x, y;
        cin >> a >> b >> x >> y;
        a--;
        b--;
        int lca = LCA(a, b);
        queries[i] = {a,b,x,y,lca,computePath(a,b,lca).first};
    }
    //usagé donc on nettoie
    pathsum.reset();
    occurence.reset();
    parallel_binary_search();
    
    for(int i = 0;i<q;i++){
        ll tot = queries[i].totalSum-ans[i].first;
        if(queries[i].x>=ans[i].second && queries[i].y>=tot)
            cout<<queries[i].x-ans[i].second<<'\n';
        else
            cout<<-1<<'\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...