2017多校训练赛第九场 HDU 6162(LCA+Treap解法)

来源:互联网 发布:个人定位软件 编辑:程序博客网 时间:2024/06/06 08:58

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 31 2 1 3 21 22 43 12 54 5 1 31 1 1 13 5 2 3

Sample Output

7 1 4

Source

2017 Multi-University Training Contest - Team 9



        第一道比较实用的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 100010using 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;//Treapnamespace 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;}

原创粉丝点击