【8.29校内测试】【分层图最短路】【数学公式推导?】

考场上一眼就觉得是$A_star$!赶快拍完又调了半天结果大样例卡成粑粑...所以索很玄学要少用啊...

考后看到$fyt$的代码简直就crazy叻!!不就是个分层图最短路DP吗!!所以水题刷的不够多啊...

定义状态$dp[u][k]$表示当前到$u$点,途径了$k$个点能走的最短距离,因为距离要小于$L$当然是越短越好啊!就可以刷表转移了...

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<queue>
#define LL long long
using namespace std;

LL dp[5005][5005];

int V, M, N, E, S, T;
LL L;

struct Node {
    int u, cnt;
    Node ( int u = 0, int cnt = 0 ) :
        u ( u ), cnt ( cnt ) { }
};

struct Edge {
    int v, nex; LL w;
    Edge ( int v = 0, LL w = 0, int nex = 0 ) :
        v ( v ), w ( w ), nex ( nex ) {    }
} edge[10005];

int stot, h[5005];
void add ( int u, int v, LL s ) {
    edge[++stot] = Edge ( v, s, h[u] );
    h[u] = stot;
}

int vis[5005][5005];
void Spfa ( ) {
    queue < Node > q;
    q.push ( Node ( S, 0 ) );
    memset ( dp, 0x3f3f3f3f, sizeof ( dp ) );
    vis[S][0] = 1; dp[S][0] = 0;
    while ( !q.empty ( ) ) {
        Node x = q.front ( ); q.pop ( );
        int u = x.u, cnt = x.cnt;
        vis[u][cnt] = 0;
        for ( int i = h[u]; i; i = edge[i].nex ) {
            int v = edge[i].v;
            if ( dp[v][cnt+1] > dp[u][cnt] + edge[i].w && dp[u][cnt] + edge[i].w <= L ) {
                dp[v][cnt+1] = dp[u][cnt] + edge[i].w;
                if ( !vis[v][cnt+1] ) {
                    vis[v][cnt+1] = 1;
                    q.push ( Node ( v, cnt + 1 ) );
                }
            }
        }
    }
}

int main ( ) {
    freopen ( "park.in", "r", stdin );
    freopen ( "park.out", "w", stdout );
    scanf ( "%d%d%d%d%I64d", &V, &M, &N, &E, &L );
    S = 0, T = V + 1;
    for ( int i = 1; i <= M; i ++ )    {
        int s; LL a;
        scanf ( "%d%I64d", &s, &a );
        add ( S, s, a );
    }
    for ( int i = 1; i <= N; i ++ ) {
        int t; LL b;
        scanf ( "%d%I64d", &t, &b );
        add ( t, T, b );
    }
    for ( int i = 1; i <= E; i ++ ) {
        int u, v; LL s;
        scanf ( "%d%d%I64d", &u, &v, &s );
        add ( u, v, s );
    }
    Spfa ( );
    for ( int i = V + 1; i >= 0; i -- ) 
        if ( dp[T][i] <= L ) {
            if ( i ) printf ( "%d", i - 1 );
            else printf ( "0" );
            break;
        }
    return 0;
}

数据范围和题意好分块啊...QAQ,大样例欺骗我!!

正解是推公式,通过部分预处理和部分$O(1)$计算得到,我们发现,所有点满足条件的位置$des[i]$是单调不降的,而且第一个满足了后面一定都满足。所以首先想到每次询问,二分出最后一个$des[i]$在$R$以内的$pos$,$L$到$pos$的贡献都要计算在内。

然后发现,我们要求的实际上是$\sum_{i=L}^{pos}{\frac{(des[i]-i+R-i)(R-des[i]+1)}{2}}$,后面的一坨是已经化简过的所有$j-i$的等差数列。

拆开得到一堆东西,只和$des[i]$和$i$有关的可以$O(n)$预处理,只和$R$有关的可以直接$*(pos-L+1)$,和$i$和$R$有关的用等差数列$O(1)$算,直接得出答案。

预处理$des[i]$的时候用划窗。

注意long long!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;

LL n, m, q, a[100005];
LL color[100005], Q[100005], des[100005];
LL sum[100005];

LL erfen ( LL L, LL R ) {
    LL l = L, r = R, ans = L - 1;
    while ( l <= r ) {
        LL mid = ( l + r ) >> 1;
        if ( des[mid] <= R && des[mid] ) ans = mid, l = mid + 1;
        else r = mid - 1;
    }
    return ans;
}

int main ( ) {
    freopen ( "plan.in", "r", stdin );
    freopen ( "plan.out", "w", stdout );
    scanf ( "%I64d%I64d%I64d", &n, &m, &q );
    for ( LL i = 1; i <= n; i ++ )    scanf ( "%I64d", &a[i] );
    LL h = 1, t = 0, cnt = 0;
    for ( LL i = 1; i <= n; i ++ ) {
        Q[++t] = i; color[a[i]] ++; 
        if ( color[a[i]] == 1 ) cnt ++;
        while ( cnt == m ) {
            des[Q[h]] = t; color[a[Q[h]]] --;
            if ( color[a[Q[h++]]] == 0 ) cnt --;
        }
    }
    for ( LL i = 1; i <= n; i ++ ) {
        LL tmp = -1LL * des[i] * des[i] + des[i] + 1LL * 2 * i * des[i] - 1LL * 2 * i;
        sum[i] = sum[i-1] + tmp;
    }
    for ( LL i = 1; i <= q; i ++ ) {
        LL L, R;
        scanf ( "%I64d%I64d", &L, &R );
        LL pos = erfen ( L, R );
        if ( pos < L ) printf ( "0\n" );
        else {
            LL ans;
            LL tmp1 = 1LL * ( R * R  + R ) * ( pos - L + 1 );
            LL tmp2 = -1LL * ( L + pos ) * ( pos - L + 1 ) * R;
            LL tmp3 = 1LL * ( sum[pos] - sum[L-1] );
            ans = 1LL * ( tmp1 + tmp2 + tmp3 ) / 2;
            printf ( "%I64d\n", ans );
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wans-caesar-02111007/p/9555862.html