• 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
    ", 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
    ", ans);
    	return 0;
    }
    

    权值线段树带离散化有风险,做题需谨慎啊

    禁止诸如开发者知识库/布布扣/码迷/学步园/马开东等 copy 他人博文乃至博客的网站转载 ,用户转载请注明出处:https://www.cnblogs.com/xcysblog/
  • 相关阅读:
    洛谷 P1396 营救(二分答案)
    洛谷 P1807 最长路(toposort)
    洛谷 P1137 旅行计划(toposort)
    [bzoj3032][TYVJ P1924]七夕祭(环形均分纸牌,货仓选址)
    BZOJ3043 IncDec Sequence (差分)
    BZOJ2200 道路与航线(dijk+拓扑排序)
    P1462 通往奥格瑞玛的道路(二分+dijkstra)
    P1020 导弹拦截 O(nlogn)做法
    背包整理(01背包,完全背包,多重背包,分组背包)(待更新)
    [模板]树链剖分
  • 原文地址:https://www.cnblogs.com/xcysblog/p/9670550.html
Copyright © 2020-2023  润新知