zoukankan      html  css  js  c++  java
  • 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/
  • 相关阅读:
    【3.1】学习C++之再逢const
    【8】学习C++之this指针
    【7】学习C++之类的构造函数
    【6】学习C++之类的实例化及访问
    【5】学习C++之类的概念
    【4】学习C++之内存管理
    【3】学习C++之const关键字的使用
    【2】学习C++之引用
    【C#第一天】数据相关
    【1】学习C++时,一些零散知识点01
  • 原文地址:https://www.cnblogs.com/xcysblog/p/9670550.html
Copyright © 2011-2022 走看看