1065 最小正子段和 二分答案 + 判定

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1065

我的思路比较笨,我是直接二分那个答案mid

然后进行一次O(nlogn)的判定,如果能找到一个区间的和比mid小的,(当然这个区间的和也是要大于0),那就return true

进行判断如下:

处理出前缀和dp[i],对于每一个i

目标是:在前i - 1个中,查看是否有这样的一个x,使得,dp[i] - x    <=   mid,&& dp[i] - x >= 1

二分找x,把前i - 1个东西压去set中,然后二分找x >= dp[i] - mid的那个x,判定即可。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <bitset>
const int maxn = 50000 + 20;
LL dp[maxn];
int n;
set<LL>ss;
bool check(LL val) {
    if (dp[1] <= val && dp[1] >= 1) return true;
    ss.clear();
    ss.insert(0);
    ss.insert(dp[1]);
    LL mx = max(0LL, dp[1]);
    for (int i = 2; i <= n; ++i) {
        LL x = dp[i] - val;
        if (mx < x) {
            mx = max(mx, dp[i]);
            ss.insert(dp[i]);
            continue;
        }
        set<LL> :: iterator it = ss.lower_bound(x);
        if (dp[i] - *it >= 1) return true;
        mx = max(mx, dp[i]);
        ss.insert(dp[i]);
    }
    return false;
}
void work() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        int x;
        scanf("%d", &x);
        dp[i] = dp[i - 1] + x;
    }
//    for (int i = 1; i <= n; ++i) {
//        cout << dp[i] << " ";
//    }
//    cout << endl;
    LL be = 1, en = 1e18L;
//    cout << check(1) << endl;
    while (be <= en) {
        LL mid = (be + en) >> 1;
        if (check(mid)) {
            en = mid - 1;
        } else be = mid + 1;
    }
//    cout << be << endl;
    printf("%lld\n", be);
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    work();
    return 0;
}

时间: 2024-10-09 20:18:36

1065 最小正子段和 二分答案 + 判定的相关文章

51nod 1065 最小正子段和

题目链接:51nod 1065 最小正子段和 房教说用前缀和做,然后看了别人博客懂了后就感觉,这个真有意思... 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N = 50001; 6 const int inf = 0x3f3f3f3f; 7 pair<long long, int> sum[N]; 8 int

51nod 1065 最小正子段和 (贪心)

题目:传送门. 题意:中文题. 题解:求前缀和,并且标记每个数的下标,按照前缀和大小进行从小到大排序.随后进行遍历,如果满足下标data[i-1].id<data[i].id&&data[i-1].val<data[i].val:就更新最小值,因为是相近的,所以已经是最小值候选了,其余的绝对不可能了.那么为什么只考虑相邻的呢?因为如果A到B不能形成队列,A到C形成队列了,那么B到C一定是比A到C的数值更小,而且还一定能够形成队列(A与B不能形成队列,说明posA>posB

AC日记——最小正子段和 51nod 1065

最小正子段和 思路: 找最小的大于0的sum[j]-sum[i](j>i): 高级数据结构(splay)水过: 来,上代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 50005 #define ll long long #define INF 0x7fffffff str

51nod-1065 最小正子段和 【贪心 + 思维】

N个整数组成的序列a[1],a[2],a[3],-,a[n],从中选出一个子序列(a[i],a[i+1],-a[j]),使这个子序列的和>0,并且这个和是所有和>0的子序列中最小的. 例如:4,-1,5,-2,-1,2,6,-2.-1,5,-2,-1,序列和为1,是最小的. Input 第1行:整数序列的长度N(2 <= N <= 50000) 第2 - N+1行:N个整数 Output 输出最小正子段和. Input示例 8 4 -1 5 -2 -1 2 6 -2 Output示

51nod-1065:最小正子段和(STL)

N个整数组成的序列a11,a22,a33,…,ann,从中选出一个子序列(aii,ai+1i+1,…ajj),使这个子序列的和>0,并且这个和是所有和>0的子序列中最小的. 例如:4,-1,5,-2,-1,2,6,-2.-1,5,-2,-1,序列和为1,是最小的. Input第1行:整数序列的长度N(2 <= N <= 50000) 第2 - N+1行:N个整数Output输出最小正子段和.Sample Input 8 4 -1 5 -2 -1 2 6 -2 Sample Outp

最小正子段和

N个整数组成的序列a[1],a[2],a[3],…,a[n],从中选出一个子序列(a[i],a[i+1],…a[j]),使这个子序列的和>0,并且这个和是所有和>0的子序列中最小的. 例如:4,-1,5,-2,-1,2,6,-2.-1,5,-2,-1,序列和为1,是最小的. 收起 输入 第1行:整数序列的长度N(2 <= N <= 50000) 第2 - N+1行:N个整数 输出 输出最小正子段和. 输入样例 8 4 -1 5 -2 -1 2 6 -2 输出样例 1 思路: 虽然说

【瞎搞】 51nod 1065 最小正字段和

通道 思路:先求一下从第一位开始的到第i位的累加,4,-1,5,-2,-1,2,6,-2 => 4 3 8 6 5 7 13 11 对这个累加的数列排个序,然后只要判断邻近的两个数是否可以组成序列,比如4和3就不可以,因为4 > 3而4对应下标为0,3对应为1.4和5就可以,然后相同的前缀和取id最小,一开始丢个(0,0)进去. 代码: #include <cstdio> #include <cstring> #include <algorithm> usi

[51nod1065]最小正子段和

#include<bits/stdc++.h> using namespace std; typedef long long ll; ll a[50002]; struct node{ ll sum,idx; }nn[50002]; const ll inf=1ll<<61; bool cmp(const node &a,const node &b){ return a.sum<b.sum||(a.sum==b.sum&&a.idx<b.

[乱写]关于二分答案

看到最小值最大/最大值最小一定要往二分答案上想,想了肯定不会吃亏. 二分答案的优越性在于正难则反的思想,如果从已知条件无法得出最优解,那么就假定一个解看是否与已知条件相悖. 部分看似要二分答案的题可以用最小生成树解决.比如模拟赛23的water,最终高度其实就是从一个格子走出去的路径上最大值的最小解.但对每一个格子二分答案显然无法接受,所以可以建图跑最小生成树直接得到每一个格子的解.类似的还有模拟赛24的star way to heaven,不断将点的坐标加入集合中直到上下边界都在集合里,生成树