51nod 1624 取余最长路

2018-08-21  本文已影响0人  fruits_

题目看这里
走的过程必然是这样的:从pos[1][1]走到pos[1][x]以至于pos[2][x],再走到pos[2][y]以至于pos[3][y],最后从pos[3][y]走到pos[3][n]。留意,这里x<=y。

假设第i层的前j项和为sum[i][j],其中i∈[1,3],j∈[1][n]。
那么这样一条转折点为x和y的路径所得的值为:
ans=sum[1][x]+(sum[2][y]-sum[2][x-1])+(sum[3][n]-sum[3][y-1])
把这个式子调整一下:
令①=sum[1][x]-sum[2][x-1];②=sum[2][y]-sum[3][y-1]+sum[3][n]
ans=①+②

相当于把x和y进行了分离,于是我们可以把①用set进行存储用以查询,穷举②。
穷举②的每一项的时候,set里有许多的①,那么哪一项才是好的呢?因为要mod P,因为所有元素都属于[0,P-1],所以:
要么让①+②最接近P,要么让①+②最接近2P,这样一模剩下来的才大。故而:

情况1:找<(p-②),且最接近这个值的,这样加起来会和会最接近P
情况2:情况1不存在,比如说set里的数都>=(p-②),那么直接找set里最大的那个就行。因为这样①+②能越过P之后尽可能往前走,使得数更大。

这里注意2个地方:
1:相减的地方可能产生负数,+P来调一下
2:这里的X是<=Y的,所以不能先把所有的①放进set再枚举②。得放一个①,②从前面的①构成的set里找。

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define lowb lower_bound
const int maxN = 1e5 + 5;

ll N, P, sum[4][maxN];
set<ll> st;

int main() {
    scanf("%lld%lld", &N, &P);
    ll t;
    for (int i = 1; i <= 3; ++i) {
        sum[i][0] = 0;
        for (int j = 1; j <= N; ++j) {
            scanf("%lld", &t);
            sum[i][j] = (sum[i][j - 1] + t) % P;
        }
    }
    st.clear();
    ll ans = 0;
    for (int y = 1; y <= N; ++y) {
        st.insert((sum[1][y] - sum[2][y - 1] + P) % P);

        ll cur = (sum[2][y] - sum[3][y - 1] + sum[3][N] + P) % P;
        t = P - cur;
        set<ll>::iterator it = st.lowb(t);
        if (it != st.begin())
            ans = max(ans, (*(--it) + cur) % P);
        else
            ans = max(ans, (*(--st.end()) + cur) % P);
    }
    printf("%lld\n", ans);
    return 0;
}
上一篇下一篇

猜你喜欢

热点阅读