CF1042D Petya and Array
这题有两种做法 大概都是 O(nlogn) 的
解法一:权值线段树
先列一下式子发现要求的就是 对于所有的 R ,求出满足 sum[R] < sum[L - 1] + t 的 L 的个数
这可以权值线段树做,就像是这道题
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cctype> #include<cstdio> #define lson t[x].ch[0] #define rson t[x].ch[1] using namespace std; typedef long long ll; const int MAXN = 200005; struct Node{ int ch[2]; ll sum; Node() {sum = 0;} }t[MAXN << 2]; int n, ptr, top, sizb; ll T, res; int a[MAXN]; ll pre[MAXN], uni[(MAXN << 1) + 2333]; inline int rd() { register int x = 0; register char c = getchar(); register bool f = false; while(!isdigit(c)) { f = (c == '-'); c = getchar(); } while(isdigit(c)) { x = x * 10 + (c ^ 48); c = getchar(); } return f ? -x : x; } inline ll rdll() { register ll x = 0ll; register char c = getchar(); register bool f = false; while(!isdigit(c)) { f = (c == '-'); c = getchar(); } while(isdigit(c)) { x = x * 10ll + (c ^ 48); c = getchar(); } return f ? -x : x; } int build(int l, int r) { int x = ++ptr; t[x].sum = 0; if(l == r) return x; int mid = ((l + r) >> 1); lson = build(l, mid); rson = build(mid + 1, r); return x; } inline void pushup(int x) { t[x].sum = t[lson].sum + t[rson].sum; return; } void update(int dst, int l, int r, int x) { if(l == r) { ++t[x].sum; return; } int mid = ((l + r) >> 1); if(dst <= mid) update(dst, l, mid, lson); else update(dst, mid + 1, r, rson); pushup(x); return; } ll query(int L, int R, int l, int r, int x) { if(L <= l && r <= R) return t[x].sum; int mid = ((l + r) >> 1); ll ans = 0ll; if(L <= mid) ans = query(L, R, l, mid, lson); if(mid < R) ans += query(L, R, mid + 1, r, rson); return ans; } int main() { n = rd(); T = rdll(); for(int i = 1; i <= n; ++i) { a[i] = rd(); pre[i] = pre[i - 1] + a[i]; uni[++top] = pre[i] + 1; uni[++top] = pre[i] + T; } uni[++top] = T; sort(uni + 1, uni + top + 1); sizb = unique(uni + 1, uni + top + 1) - uni - 1; build(1, sizb); update(lower_bound(uni + 1, uni + sizb + 1, T) - uni, 1, sizb, 1); for(int i = 1; i <= n; ++i) { int dstR = lower_bound(uni + 1, uni + sizb + 1, pre[i] + 1) - uni; int dst = lower_bound(uni + 1, uni + sizb + 1, pre[i] + T) - uni; res += query(dstR, sizb, 1, sizb, 1); update(dst, 1, sizb, 1); } printf("%lld\n", res); return 0; }
解法二:分治
此题也可分治,每次分治下去,只计算跨过 mid 的答案个数
算出 [l, mid] 的后缀和 suf,算出 [mid + 1, r] 的前缀和 pre
并将两个数组排序,排序后就可以保证其中一个值递增时合法的另一个值是递减的,就可以双指针扫了
如果写基数排序的话它就是 O(nlogn) 的了
代码:
#include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cctype> #include<cstdio> using namespace std; typedef long long ll; const int MAXN = 200005; int n; ll t, ans; int a[MAXN]; ll suf[100005], pre[MAXN]; inline int rd() { register int x = 0; register char c = getchar(); register bool f = false; while(!isdigit(c)) { f = (c == '-'); c = getchar(); } while(isdigit(c)) { x = x * 10 + (c ^ 48); c = getchar(); } return f ? -x : x; } inline ll rdll() { register ll x = 0ll; register char c = getchar(); register bool f = false; while(!isdigit(c)) { f = (c == '-'); c = getchar(); } while(isdigit(c)) { x = x * 10ll + (c ^ 48); c = getchar(); } return f ? -x : x; } void dvdcq(int l, int r) { if(l == r) { ans += (a[l] < t); return; } int mid = ((l + r) >> 1); suf[mid] = a[mid]; pre[mid] = 0; for(int i = mid - 1; i >= l; --i) suf[i] = suf[i + 1] + a[i]; for(int i = mid + 1; i <= r; ++i) pre[i] = pre[i - 1] + a[i]; sort(suf + l, suf + mid + 1); sort(pre + mid + 1, pre + r + 1); int sp = l, pp = r; for( ; sp <= mid; ++sp) { while(pp >= mid + 1 && suf[sp] + pre[pp] >= t) --pp; ans += (ll)(pp - mid); } dvdcq(l, mid); dvdcq(mid + 1, r); return; } int main() { n = rd(); t = rdll(); for(int i = 1; i <= n; ++i) a[i] = rd(); dvdcq(1, n); printf("%lld\n", ans); return 0; }
权值线段树带离散化有风险,做题需谨慎啊
禁止诸如开发者知识库/布布扣/码迷/学步园/马开东等 copy 他人博文乃至博客的网站转载
,用户转载请注明出处:https://www.cnblogs.com/xcysblog/