zoukankan      html  css  js  c++  java
  • P4915 帕秋莉的魔导书

    题目描述:

    洛谷
    魔导书是一种需要钥匙才能看得懂的书,然而只有和书写者同等或更高熟练度的人才能看得见钥匙。因此,每本魔导书都有它自己的等级 \(a_i\),同时它也有自己的知识程度为 \(w_i\)​,现在我们想要知道,一个等级为 \(b_i\) 的生物(...),可以从这些魔导书中得到多少知识。

    然而不幸的是,每个生物并不知道自己确切的等级,只有一个等级的大致范围,你需要计算出这个生物获得知识程度的期望值。

    数据范围: 对于 100% 的数据,保证 \(1\leq n,m\leq 10^5\),对于其他数字,保证在 32 位带符号整数范围内(保证运算中所有的数均在 \(−2^{63}∼2^{63}−1\) 内)。

    solution

    首先,题目让我们求的是这个柿子
    \(\large \displaystyle\sum_{i=x}^{y} tot[i] \over (y-x+1)\)

    \(\large \displaystyle\sum_{i=x}^{y} tot[i] \over y-x+1\)

    \(tot[i]\)\(i\) 这个等级能看到的知识程度的总和。

    我们可以以等级为区间,知识程度为权值。

    那不就是区间和除以区间长度了吗?

    看到区间和,我们就可以想到用线段树来维护。

    但等级可能会达到2^32-1,所以我们考虑一下动态开点或者离散化.

    当新添一本书后,等级大于他的都可以看到,就相当于从这本书的等级到inf的区间,加上这本书的知识程度。

    查询操作,就是区间和除以区间长度就OK了。

    一定要注意开 long long(不开long long 见祖宗)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define LL long long
    const int inf = 2147483536;
    int n,m,x,y,opt,root,tot;
    inline int read()
    {
    	int s = 0, w = 1; char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-') w =  -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s  = s * 10+ch -'0'; ch = getchar();}
    	return s * w;
    }
    struct Tree
    {
    	struct node{
    		int lc,rc;
    		LL sum,add;
    	}tr[10000010];
    	void down(int o,int l,int r)//下放操作
    	{
    		if(tr[o].add)
    		{
    			int mid = (l+r)>>1;
    			if(!tr[o].lc) tr[o].lc = build();//如果没有子节点就新建一个
    			if(!tr[o].rc) tr[o].rc = build();
    			tr[tr[o].lc].add += tr[o].add;//正常的下放操作
    			tr[tr[o].rc].add += tr[o].add;
    			tr[tr[o].lc].sum += 1LL * tr[o].add * (mid-l+1);
    			tr[tr[o].rc].sum += 1LL * tr[o].add * (r-mid);
    			tr[o].add = 0;
    		}
    	}
    	int build()//新建一个节点
    	{
    		tot++;
    		tr[tot].lc = tr[tot].rc = tr[tot].sum = 0;
    		return tot;
    	}
    	void insert(int root,int L,int R,int x,int y,int val)//区间修改
    	{
    		if(x <= L && y >= R)
    		{
    			tr[root].add += val;
    			tr[root].sum +=1LL * val * (R-L+1);
    			return;
    		}
    		int mid = (L+R)>>1;
    		down(root,L,R);//下放标记
    		if(x <= mid)
    		{
    			if(!tr[root].lc) tr[root].lc = build();//一定要新开节点,不然就会RE
    			insert(tr[root].lc,L,mid,x,y,val);
    		}
    		if(y > mid)
    		{
    			if(!tr[root].rc) tr[root].rc = build();
    			insert(tr[root].rc,mid+1,R,x,y,val);
    		}
    		tr[root].sum = tr[tr[root].lc].sum + tr[tr[root].rc].sum;//up操作
    	}
    	LL ask(int root,int L,int R,int x,int y)//区间和
    	{
    		LL ans = 0;
    		if(x <= L && y >= R){return tr[root].sum;}
    		int mid = (L+R)>>1;
    		down(root,L,R);
    		if(!tr[root].lc) tr[root].lc = build();
    		if(!tr[root].rc) tr[root].rc = build();
    		if(x <= mid) ans += ask(tr[root].lc,L,mid,x,y);
    		if(y > mid) ans += ask(tr[root].rc,mid+1,R,x,y);
    		return ans;
    	}
    }tree;
    int main()
    {
    	n = read(); m = read();
    	root = tree.build();
    	for(int i = 1; i <= n; i++)
    	{
    		x = read(); y = read();
    		tree.insert(root,1,inf,x,inf,y);//从x到inf区间加y
    	}
    	for(int i = 1; i <= m; i++)
    	{
    		opt = read(); x = read(); y = read();
    		if(opt == 1)
    		{
    			LL tmp = tree.ask(root,1,inf,x,y);
    			double ans = (double) tmp / (double)(y-x+1);
    			printf("%.4lf\n",ans);
    		}
    		if(opt == 2)
    		{
    			tree.insert(root,1,inf,x,inf,y);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    Linux命令:sed -i 解析、sed是什么、工作原理、基本语法使用、数字和正则定址、基本子命令以及最常用子命令 s 的用法
    【转】putty里面的连接key文件(ppk文件)转换为xshell里面使用的key文件
    【转】Go 中如何优雅关闭子进程?
    [转]golang 获取本机真实IP
    【转】prometheus数据写入TDengine
    怎么查看redhat的版本
    【转】YML是什么
    [转]为什么要进行URL编码
    [转]Ubuntu 上 Yarn 安装
    【转】docker -v 和Dockerfile 中VOLUME 区别
  • 原文地址:https://www.cnblogs.com/genshy/p/13428564.html
Copyright © 2011-2022 走看看