ZOJ 3724 Delivery 树状数组好题

虽然看起来是求最短路,但因为条件的限制,可以转化为区间求最小值。

对于一条small path [a, b],假设它的长度是len,它对区间[a, b]的影响就是:len-( sum[b]-sum[a-1] );(使区间[a,b]的原有长度变长或者变短,变长没有意义,所以我们只考虑变短的情况),因为只能选择一条small path,所以对于每个查询[u, v],就是要选择在区间[u, v]内,让原有长度减少最多的那条small path,即求区间最小值。

离线处理:

将查询和small path放在一起排序,按u从大到小,v从小到大排。

因为我们要从后往前扫,对于每个查询[u, v],我们应当保证在本查询之前的所有[u', v']已经更新进去。

当u < v时,u < u', v' < v,树状数组minv[i]中记录的是以i为区间右端点的所有small path中len-( sum[b]-sum[a-1] )的最小值,答案为[u, v]的区间和+该最小值。

当u > v时,v' < v, u < u',树状数组minv[i]中记录的是从i点走回i点(走了一个圈)的最小值。答案为最小值减去[u, v]的区间和。

------------------------以下吐槽--------------------------

本来以为不是很难的一道树状数组,结果折腾了一晚上……主要是对查询中u>v的情况处理总是各种出问题,之前我是想把1-N扩大一倍变成1-2N,这样对于查询中u>v的情况就可以转换成查询[ u, N+v ]。事实证明这样转换是不对的……画画图看看就知道为啥了。

然后我又想关于N轴对称把点翻转过去,还是1-2N,只不过把查询变成了[ u, 2N - v ]。还是各种出错orz……

最后实在没办法,老老实实的分开处理了……

 

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

#define LL long long int

using namespace std;

const int MAXN = 100000 << 1;
const int INF = 1000000;
long long int one = 1;

LL sum[MAXN]; //区间和
LL minv[MAXN];
LL ans[200100];

int N, M;

struct node
{
    int l, r;
    LL val;
    int id;
};

node D[ MAXN << 1 ];

int lowbit( int x )
{
    return x & ( -x );
}

void Add( int x, LL val )
{
    while ( x <= N )
    {
        minv[x] = min( minv[x], val );
        x += lowbit(x);
    }
    return;
}

LL Query( int x )
{
    LL res = one << 60;
    while ( x > 0 )
    {
        res = min( res, minv[x] );
        x -= lowbit(x);
    }
    return res;
}

bool cmp( node a, node b )
{
    if ( a.l != b.l ) return a.l > b.l;
    if ( a.r != b.r ) return a.r < b.r;
    return a.id < b.id;
}

int main()
{
    //freopen( "in.txt", "r", stdin );
    //freopen( "out.txt", "w", stdout );
    while ( scanf( "%d%d", &N, &M ) == 2 )
    {
        memset( minv, 0, sizeof(minv) );

        sum[0] = 0;
        for ( int i = 1; i < N; ++i )
        {
            scanf( "%I64d", &sum[i] );
            sum[i] += sum[i - 1];
        }
        sum[N] = INF;

        int cnt = 0;
        for ( int i = 0; i < M; ++i )
        {
            int a, b, c;
            scanf( "%d%d%d", &a, &b, &c );
            LL tmp;
            if ( a > b )
                tmp = (LL)c + sum[a - 1] - sum[b - 1];
            else
                tmp = (LL)c - sum[b - 1] + sum[a - 1];
            D[cnt].l = a;
            D[cnt].r = b;
            D[cnt].val = tmp;
            D[cnt].id = -1;
            ++cnt;
        }

        int Q;
        scanf( "%d", &Q );
        for ( int i = 0; i < Q; ++i )
        {
            int a, b;
            scanf( "%d%d", &a, &b );
            D[cnt].id = i;
            D[cnt].val = 0;
            D[cnt].l = a;
            D[cnt].r = b;
            ++cnt;
        }

        memset( ans, 0, sizeof(ans) );//之前少了这个,一直WA…好像上组数据会影响到下组数据中u=v的情况
        sort( D, D + cnt, cmp );

        for ( int i = 0; i < cnt; ++i )
        {
            if ( D[i].l < D[i].r )
            {
                Add( D[i].r, D[i].val );
                if ( D[i].id != -1 )
                    ans[ D[i].id ] = sum[ D[i].r - 1 ] - sum[ D[i].l - 1 ] + Query( D[i].r );
            }
        }

        for ( int i = 0; i <= N; ++i )
            minv[i] = one << 60;

        for ( int i = 0; i < cnt; ++i )
        {
            if ( D[i].l > D[i].r )
            {
                if ( D[i].id == -1 ) Add( D[i].r, D[i].val );
                if ( D[i].id != -1 )
                    ans[ D[i].id ] = Query( D[i].r ) - sum[ D[i].l - 1 ] + sum[ D[i].r - 1 ];
            }
        }

        for ( int i = 0; i < Q; ++i )
            printf( "%d\n", (int)ans[i] );
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/GBRgbr/p/3269004.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值