zoj 3724 树状数组经典


问题:n个点,对于每个点i,都有一条连向i+1的有向边,另外有m条其他的有向边,有q个询问(u,v)求u到v的最短路

将m条有向边和q个询问对所表示的点对一起排序,(u,v)u大的排前,u一样的v小的排前,u和v一样大的先处理插入的再处理询问的;
边插入边询问;
树状数组里存的是右端点在v之后的询问的结果的差值
(1)对于(u,v)u<v的这种边,保证在查询到(u,v)之前,所有的(u2,v2)u<u2<v2<v都已经插入好,同时,(u2,v2) u2<u或者(u2=u && v2>v) 的那些还没插入,树状数组存mat[u1][v1]-(d[v1]-d[u1])的最小值,查询时在加上(d[v]-d[u])
(2)对于(u,v)u>v的这种边,保证在查询到(u,v)之前,所有(u2,v2) v2<v<u<u2的都已经插入好,同时,(u2,v2) v<v2<u2<u那些不能插入。树状数组里存的是mat[u2][v2]+(d[u2]-d[v2]),查询再减去(d[u]-d[v])
所以要通过排序来操作
#include<iostream>
#include<cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long
ll b[105000];
int N;
int lowbit(int x)
{
    return x&(-x);
}
void upd(int x,ll val)
{
    while(x<=N)
    {
        b[x]=min(b[x],val);
        x+=lowbit(x);
    }
}
ll query(int x)
{
    ll res=(1LL<<60);
    while(x)
    {
        res=min(res,b[x]);
        x-=lowbit(x);
    }
    return res;
}
struct Point
{
    int u,v,kind;
    ll ind;
}p[400500];
bool cmp(Point left,Point right)
{
    if(left.u==right.u && left.v==right.v)
        return left.kind<right.kind; // insert before query
    if(left.u==right.u)
        return left.v<right.v;
    return left.u>right.u;
}
ll s[100500];
int ans[200500];
int main ()
{
    int n,m,q,u,v,ind;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1;i<n;++i)
        scanf("%d",&ind),s[i+1]=ind+s[i];
        for(int i=1;i<=m;++i)
        {
            scanf("%d%d%lld",&p[i].u,&p[i].v,&p[i].ind);
            p[i].kind=0;
        }
        scanf("%d",&q);
        for(int i=1;i<=q;++i)
        {
            scanf("%d%d",&p[m+i].u,&p[m+i].v);
            p[m+i].kind=1;
            p[m+i].ind=i;
        }
        sort(p+1,p+1+m+q,cmp);
        N=n;
        memset(ans,0,sizeof(ans)); // 一些询问u=v,不初始化都为0,会错
        memset(b,0,sizeof(b));
        for(int i=1;i<=m+q;++i)
        {
            if(p[i].kind==0 && p[i].u<p[i].v)
                upd(p[i].v,p[i].ind-(s[p[i].v]-s[p[i].u]));
            if(p[i].kind==1 && p[i].u<p[i].v)
                ans[p[i].ind]=s[p[i].v]-s[p[i].u]+query(p[i].v);
        }
        for(int i=1;i<=n;++i)
            b[i]=(1LL<<60);
        for(int i=1;i<=m+q;++i)
        {
            if(p[i].kind==0 && p[i].u>p[i].v)
                upd(p[i].v,p[i].ind+(s[p[i].u]-s[p[i].v]));
            if(p[i].kind==1 && p[i].u>p[i].v)
                ans[p[i].ind]=query(p[i].v)-(s[p[i].u]-s[p[i].v]);
        }
        for(int i=1;i<=q;++i)
            printf("%d\n",ans[i]);
    }
    return 0;
}






















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值