1065 最小正子段和 二分答案 + 判定
Posted stupid_one
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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; }
这题的正解是:
预处理前缀和,然后按从小到大排序,相同时再按位置从小到大排序
比如前缀和是,3、1、2
排序后是1(2)、2(3)、3(1),括号的是位置
那么3不能作为贡献,因为他前面没有比他位置小的数字。
一开始以为这样找也是O(n^2),但是发现好像只需要比较相邻的两位就可以,
给个数据
4
-1、-1、-1、8
#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; struct node { LL val; int pos; bool operator < (const struct node & rhs) const { if (val != rhs.val) return val < rhs.val; else return pos < rhs.pos; } }a[maxn]; void work() { int n; scanf("%d", &n); for (int i = 1; i <= n; ++i) { int x; scanf("%d", &x); a[i].val = a[i - 1].val + x; a[i].pos = i; } sort(a + 1, a + 1 + n); LL ans = 1e18L; if (a[1].val >= 1) ans = a[1].val; for (int i = 2; i <= n; ++i) { if (a[i].val >= 1) ans = min(a[i].val, ans); if (a[i].pos > a[i - 1].pos && a[i].val - a[i - 1].val >= 1) { ans = min(ans, a[i].val - a[i - 1].val); } } cout << ans << endl; } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif work(); return 0; }
以上是关于1065 最小正子段和 二分答案 + 判定的主要内容,如果未能解决你的问题,请参考以下文章