zoukankan      html  css  js  c++  java
  • 序列 [树状数组+离散化]

    序列

    题目描述

    给定两个长度为n的序列 (a, b) 。你需要选择一个区间([l,r]),使得 (a_l+…+a_rgeqslant 0)(b_l+…+b_rgeqslant 0)。最大化你选择的区间长度。

    输入格式

    第一行一个整数 (n),第二行 (n) 个整数 (a_1 o a_n),第三行 (n) 个整数 (b_1 o b_n)

    输出格式

    一行一个整数表示(max(r-l+1))。保证至少有一个区间满足条件。

    样例

    样例输入

    5
    2 -4 1 2 -2
    -2 3 1 -3 1
    

    样例输出

    1
    

    数据范围与提示

    对于 (20\%) 的数据,(nleqslant 5000)

    对于 (60\%) 的数据,(nleqslant 10^5)

    对于 (100\%) 的数据,(1leqslant nleqslant 10^6,|a_i|, |b_i|leqslant 10^9)。 数据有一定梯度。

    分析

    看到题目就能得到三个柿子,也就是个三维偏序:

    [suma_r-suma_lgeqslant 0\ sumb_r-sumb_lgeqslant 0\ r-lgeqslant 0]

    但是三个不等式是非常难以维护的,所以我们考虑简化一下这三个式子。

    我们可以首先让一个条件满足,我们先按 (suma) 的大小升序排序,这样第一个式子就绝对满足了,我们只需要考虑剩下的二维偏序问题。

    因为 (sumb) 的范围肯定要比 (long long) 还大,所以我们需要离散化来进行存储。因为我们需要满足在 (sumb) 的条件满足的情况下,找到当前这个位置向左拓展的最小的左边界,这个区间的长度就是答案。

    根据上边所说的,我们就可以使用树状数组来维护每个 (sumb) 的最小左端点,用 (sumb) 当作下标(离散化之后的),用当前这个值的位置来更新值,在查询的时候查询这个位置即可,答案就是当前值的位置减去查询到的左端点,取 (max) 即可。

    代码

    
    
    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const int maxn = 1e6+10;
    ll suma[maxn],sumb[maxn];
    struct Node{
    	ll a,b,id;
    }jl[maxn];
    ll n;
    inline ll read(){
    	ll s = 0,f = 1;
    	char ch = getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch))s=s*10+ch-'0',ch=getchar();
    	return s*f;
    }
    int t[maxn];
    int lowbit(int x){
    	return x & -x;
    }
    void modify(int x,int val){//修改最小值
    	while(x <= n){
    		t[x] = min(t[x],val);
    		x += lowbit(x);
    	}
    }
    int query(int x){//查询最小左端点
    	int res = 0x3f3f3f3f;
    	while(x){
    		res = min(res,t[x]);
    		x -= lowbit(x);
    	}
    	return res;
    }
    bool cmp(Node x,Node y){//按suma排序
    	return x.a < y.a;
    }
    int main(){
    	freopen("B.in","r",stdin);
    	freopen("B.out","w",stdout);
    	memset(t,0x3f,sizeof(t));//因为要维护最小的左端点,所以初始化极大值
    	n = read();
    	for(register int i = 1;i <= n;++i){
    		ll x = read();
    		jl[i].id = i;
    		suma[i] = suma[i-1] + x;
    		jl[i].a = suma[i];
    	}
    	for(register int i = 1;i <= n;++i){
    		ll x = read();
    		sumb[i] = sumb[i-1] + x;
    		jl[i].b = sumb[i];
    	}
    	int ans = 1;
    	for(register int i = 1;i <= n;++i){
    		if(sumb[i] >= 0 && suma[i] >= 0){//先提前处理一下,不然会挂
    			ans = max(ans,i);
    		}
    	}
    	sort(jl+1,jl+n+1,cmp);//按suma排序,去掉一个限制条件
    	//以下两行离散化
    	sort(sumb+1,sumb+n+1);
    	int q = unique(sumb+1,sumb+n+1)-sumb-1;
    	for(int i=1;i<=n;++i){
    		int pos = lower_bound(sumb+1,sumb+q+1,jl[i].b) - sumb;//找到这个sumb的位置(也就是离散化后的值)
    		modify(pos,jl[i].id);//修改
    		ans = max((ll)ans,jl[i].id - query(pos));//查询并更新答案
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    01 用CE查找游戏基址
    过QQ游戏大厅的SX保护 Evil0r's Blog 博客频道 CSDN_NET
    CODE人物坐标
    显示Combobox1中怪物名称列表.txt
    模拟安键F5 SendKeys
    红蓝保护(按键模拟)
    喊话CALL
    CODE Unicode roleName_字符类型数据 人物名子
    显示人物主要信息
    捡物 call 代码注入
  • 原文地址:https://www.cnblogs.com/Vocanda/p/13504378.html
Copyright © 2011-2022 走看看