• P5459 [BJOI2016]回转寿司 题解


    P5459 [BJOI2016]回转寿司 题解

    间隙

    前置知识

    • 前缀和,权值线段树,动态开点

    如果您还不会权值线段树跟动态开点的话,推荐去看一下这个教程

    大致题意

    给一个序列,现从中取出一段连续子序列,使其子序列内数值总和(a)满足(Lle ale R)

    求总方案数。

    分析

    区间求和,很容易先联想到前缀和

    不妨先设(sum[i])为前(i)个数的前缀和

    易得式子:

    (L le sum[r] - sum[l-1] le R)

    移项一下

    $sum[r]-L le sum[l-1] le sum[r]-R $

    这样原问题就转化为了在区间([sum[r]-L,sum[r]-R])中有多少个(sum[l-1])((l in[1,r]) )

    每一个(r)也就相当于是查询区间([sum[r]-L,sum[r]-R])(sum[l-1])的总和((l in[1,r]) )

    可以使用权值线段树(+)动态开点来维护。

    代码实现

    (1)~(n)枚举(r)的值,把每一个(r)当作一次"查询"

    同时不要忘记在进行下一次"查询" 前把 (l) 的值 "更新"(指插入新的值)

    具体的注释里有讲

    
    #include<bits/stdc++.h>
    using namespace std;
    long long MAXN = 1e10;
    const int N = 1e5+5;
    long long sum[N];//前缀和 
    int n,l,r;
    long long ans = 0;
    int tot = 0;
    struct st{
    	int l,r,sum;//左儿子,右儿子,总方案数 
    }tree[N<<10];
    
    void pushup(int node){//上传操作 
    	tree[node].sum = tree[tree[node].l].sum+tree[tree[node].r].sum;
    }
    void insert(int &node,long long x,long long l = -MAXN , long long r = MAXN){//更新 注意,l的初始值要设成负数,一开始在这里卡了好久kk 
    	
    	if(!node) node = ++tot;//动态开点 
    	if(l==r){//如果为根节点 
    		tree[node].sum++;
    		return;
    	}
    	long long mid = (l + r)>>1;
    	if(x<=mid) update(tree[node].l,x,l,mid);
    	else update(tree[node].r,x,mid+1,r);
    	pushup(node);//更新父节点的值 
    	
    }
    
    long long query(int &node,long long x,long long y,long long l =-MAXN,long long r = MAXN){//查询操作 
    	if(!node) node = ++tot;//动态开点 
    	if(x<=l&&y>=r){//包含在查询范围内 
    		return tree[node].sum;
    	}
    	long long res = 0;
    	long long mid = (l+r)>>1;
    	if(x<=mid) res+=query(tree[node].l,x,y,l,mid); 
    	if(y>mid) res+=query(tree[node].r,x,y,mid+1,r); 
    	return res;
    }
    int main(){
    	int root = 0;
    	scanf("%d%d%d",&n,&l,&r);
    	for(int i=1;i<=n;i++){
    		int a;
    		scanf("%d",&a);
    		sum[i] = sum[i-1] + a;//前缀和 
    	}
    	insert(root,0);//不要忘记插入0,也就是说一个都不吃的情况 
    	for(int i=1;i<=n;i++){
    		ans+=query(root,sum[i] - r,sum[i] - l);//加方案数 
    		insert(root,sum[i]);//"更新"l的值 
    	}
    	cout<<ans;
    } 
    
    
  • 相关阅读:
    IsIconic() OnPaint里的用途
    中值滤波
    一个小学生题库生成器
    音视频同步
    [转]字符编码笔记:ASCII,Unicode和UTF8
    项目中常见bug及解决方法
    TSQL基础chp10可编程对象学习笔记[上]
    使用UdpAppender时出现了“使用了与请求协议不兼容的地址”的解决办法
    .net gridview 任意单击某行跳转到新的页面,并且新页面的参数来自于与gridview中的不可见字段
    数组去重的四种方法
  • 原文地址:https://www.cnblogs.com/xcxc82/p/13438974.html
Copyright © 2020-2023  润新知