1. 程式人生 > >2017多校訓練賽第九場 HDU 6162(LCA+Treap解法)

2017多校訓練賽第九場 HDU 6162(LCA+Treap解法)

Ch’s gift

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1091    Accepted Submission(s): 412

Problem Description

Mr. Cui is working off-campus and he misses his girl friend very much. After a whole night tossing and turning, he decides to get to his girl friend's city and of course, with well-chosen gifts. He knows neither too low the price could a gift be since his girl friend won't like it, nor too high of it since he might consider not worth to do. So he will only buy gifts whose price is between [a,b].
There are n cities in the country and (n-1) bi-directional roads. Each city can be reached from any other city. In the ith city, there is a specialty of price ci Cui could buy as a gift. Cui buy at most 1 gift in a city. Cui starts his trip from city s and his girl friend is in city t. As mentioned above, Cui is so hurry that he will choose the quickest way to his girl friend(in other words, he won't pass a city twice) and of course, buy as many as gifts as possible. Now he wants to know, how much money does he need to prepare for all the gifts?

Input

There are multiple cases.

For each case:
The first line contains tow integers n,m(1≤n,m≤10^5), representing the number of cities and the number of situations.
The second line contains n integers c1,c2,...,cn(1≤ci≤10^9), indicating the price of city i's specialty.
Then n-1 lines follows. Each line has two integers x,y(1≤x,y≤n), meaning there is road between city x and city y.
Next m line follows. In each line there are four integers s,t,a,b(1≤s,t≤n;1≤a≤b≤10^9), which indicates start city, end city, lower bound of the price, upper bound of the price, respectively, as the exact meaning mentioned in the description above

Output

Output m space-separated integers in one line, and the ith number should be the answer to the ith situation.

Sample Input

5 3 1 2 1 3 2 1 2 2 4 3 1 2 5 4 5 1 3 1 1 1 1 3 5 2 3

Sample Output

7 1 4

Source

        第一道比較實用的Treap題……

        之前說過,這題可以用離線處理+樹鏈剖分+線段樹比較巧妙的方法過。但是,這裡還有一種比較美觀的方法,就是用Treap。

        我們知道,當沒有閾值限制的時候,就是普通的鏈上求和,對於每個點,我們記錄該點到根的權值和,記為sum[i]。那麼(x,y)路徑和就是sum[x]+sum[y]-sum[lca(x,y)]-sum[fa[lca(x,y)]]。那麼,當有閾值限制的時候,如何處理這個問題呢?

        首先,我們和之前的做法一樣,把閾值拆成[1,l-1]和[1,r]最後再兩者相減。然後我們發現,我們計算sum的時候,對於每個詢問,只需要求滿足條件的權值和。我們可以維護一棵Treap,每次dfs的時候加入一個點,dfs之後再刪除,這樣可以保證Treap中只有點i到根的所有點。然後,根據Treap的性質,我們可以在O(logN)的時間內求出滿足閾值條件的權值和,把所有的詢問計算一遍即可。然後對於詢問,我們按照普通求鏈上和的方法來拆開,再加上閾值拆成的兩個區間,每條詢問可以拆成8條小詢問。我們每個小詢問存在每個點上,當遍歷到該點的時候,順便解決這些詢問。

        實現起來需要對資料結構比較瞭解,而我則是第一次寫比較使用的Treap,調了許久算是寫對了。具體見程式碼:

#include<bits/stdc++.h>
#define LL long long
#define N 100010
using namespace std;

struct query{int val,id,on;};
vector<query> q[N];
int n,m,a[N],root;
vector<int> g[N];
LL ans[N];

struct Treap
{
    struct treap
    {
        int son[2],num,fix;
        LL sum;
    } tree[N];

    int sz;

    inline void update(int x)
    {
        if (!x) return;
        tree[x].sum=tree[x].num;
        if (tree[x].son[0]) tree[x].sum+=tree[tree[x].son[0]].sum;
        if (tree[x].son[1]) tree[x].sum+=tree[tree[x].son[1]].sum;
    }

    inline void Rotate(int &x,bool ch)
    {
        int y=tree[x].son[ch^1],z=x;
        tree[x].son[ch^1]=tree[y].son[ch];
        tree[y].son[ch]=x; x=y;
        update(z); update(y);
    }

    inline void ins(int &i,int x)
    {
        if (!i)
        {
            i=++sz;
            tree[i].fix=rand();
            tree[i].num=tree[i].sum=x;
            tree[i].son[0]=tree[i].son[1]=0;
            return;
        }
        bool ch=(x>tree[i].num);
        ins(tree[i].son[ch],x);
        if (tree[tree[i].son[ch]].fix>tree[i].fix) Rotate(i,ch^1);
        update(i);
    }

    inline void del(int &i,int x)
    {
        if (!i) return;
        if (tree[i].num==x)
        {
            if (!tree[i].son[0]) i=tree[i].son[1];
            else if (!tree[i].son[1]) i=tree[i].son[0];
            else
            {
                bool ch=(tree[tree[i].son[0]].fix>tree[i].fix);
                Rotate(i,ch); del(tree[i].son[ch],x);
            }
        } else if (x<tree[i].num) del(tree[i].son[0],x);
                             else del(tree[i].son[1],x);
        if (i) update(i);
    }

    inline LL query(int &i,int x)
    {
        LL res=0;
        if (!i) return res;
        if (x>=tree[i].num)						//閾值比當前點大則該點及其左子樹都可以加上去,右子樹繼續找
        {
            res+=tree[i].num+query(tree[i].son[1],x);
            if (tree[i].son[0]) res+=tree[tree[i].son[0]].sum;
        } else res=query(tree[i].son[0],x);				//否則在比他更小的左子樹中找
        return res;
    }
} treap;							//Treap

namespace LCA
{
    int dp[N][20],dep[N];

    inline void dfs(int x,int fa)
    {
        for(int i=0;i<g[x].size();i++)
        {
            int y=g[x][i];
            if (y==fa) continue;
            dep[y]=dep[x]+1;
            dp[y][0]=x; dfs(y,x);
        }
    }

    void ST()
    {
        for(int j=0;j<17;j++)
            for(int i=1;i<=n;i++)
                dp[i][j+1]=dp[dp[i][j]][j];
    }

    inline int lca(int u,int v)
    {
        if (u==v) return u;
        if (dep[v]>dep[u]) swap(u,v);
        for(int i=18;i>=0;i--)
            if (dep[dp[u][i]]>=dep[v]) u=dp[u][i];
        if (u==v) return u;
        for(int i=18;i>=0;i--)
            if (dp[u][i]!=dp[v][i]) u=dp[u][i],v=dp[v][i];
        return dp[u][0];
    }
}

inline void dfs(int x,int fa)						//dfs遍歷每個點
{
    treap.ins(root,a[x]);						//每次往Treap中插入一個點
    for(int i=0;i<q[x].size();i++)
    {
        query now=q[x][i];
        ans[now.id]+=(LL)now.on*treap.query(root,now.val);		//對於每一個詢問計算結果
    }
    for(int i=0;i<g[x].size();i++)
        if (g[x][i]!=fa) dfs(g[x][i],x);
    treap.del(root,a[x]);						//搜尋完畢後刪除
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        treap.sz=root=0;
        memset(ans,0,sizeof(ans));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            q[i].clear();
            g[i].clear();
        }
        for(int i=1;i<n;i++)
        {
            int u,v;srand((unsigned int)time(0));
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        LCA::dep[1]=1;
        LCA::dfs(1,0); LCA::ST();
        for(int i=1;i<=m;i++)
        {
            int x,y,l,r,lca;
            scanf("%d%d%d%d",&x,&y,&l,&r);					//每一個詢問拆成8個小詢問
            lca=LCA::lca(x,y);
            q[x].push_back(query{r,i,1});
            q[y].push_back(query{r,i,1});
            q[lca].push_back(query{r,i,-1});
            q[x].push_back(query{l-1,i,-1});
            q[y].push_back(query{l-1,i,-1});
            q[lca].push_back(query{l-1,i,1});
            q[LCA::dp[lca][0]].push_back(query{r,i,-1});
            q[LCA::dp[lca][0]].push_back(query{l-1,i,1});
        }
        dfs(1,0);
        for(int i=1;i<m;i++)
            printf("%I64d ",ans[i]);
        printf("%I64d\n",ans[m]);
    }
    return 0;
}