虽然看起来是求最短路,但因为条件的限制,可以转化为区间求最小值。
对于一条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; }