博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【NOIP2018模拟11.01】树
阅读量:5226 次
发布时间:2019-06-14

本文共 2794 字,大约阅读时间需要 9 分钟。

描述

在这里插入图片描述

题目大意

维护一个序列,支持三种操作:

1、修改一段区间,将这段区间内的所有数都andandand一个数。
2、询问区间和。
3、询问区间两两相加的平方和。
N≤10000N\leq 10000N10000


思路

显然是一道数据结构题。

毋庸置疑的,这绝对是一棵线段树。
第三个操作还是比较简单的:
∑(ai+aj)2=∑ai2+aj2+2aiaj=2∗len∗∑ai2+2∑aiaj=2∗len∗∑ai2+2(∑ai)2\sum{(a_i+a_j)^2} \\ =\sum{a_i^2+a_j^2+2a_ia_j}\\ =2*len*\sum{a_i^2}+2\sum{a_ia_j}\\ =2*len*\sum{a_i^2}+2\left(\sum{a_i}\right)^2(ai+aj)2=ai2+aj2+2aiaj=2lenai2+2aiaj=2lenai2+2(ai)2
所以只需要维护区间和还有区间平方和。
这题中,最讨厌的就是修改操作,好端端的,干嘛要来个位运算!
所以我就是着将所有的位分开来。
然而,搞不了第三个询问……
想了半天后弃疗看题解。


正解

这题正解就是直接暴力,没错,就是暴力。

对于线段树的每一个节点,维护一个值表示这段区间内或起来的和。
在修改的时候,我们就可以通过这个东西来判断这个区间里面是否有需要修改的数。
如果有,就继续往下,将它揪出来,暴力修改。
然后?然后就没了啊……
听起来这个方法的时间很诡异,实际上——
对于NNN个点,每个点一共有303030个位,又因为修改过一个位之后就再也不可能修改这个位,所以,顶多修改30N30N30N次。
乘上线段树的高度就是30Nlg⁡N30N\lg N30NlgN次。
所以时间复杂度是O(Nlg⁡Nlg⁡109)O(N\lg N\lg 10^9)O(NlgNlg109)


代码

using namespace std;#include 
#include
#include
#define N 100000#define BIt 30#define mo 998244353inline long long pow2(long long x){return x*x;}int n;int a[N+1];struct Ret{//分别是区间和、区间平方和 long long sum; int sum2;};struct Node{ int o;//表示or值 Ret s;} d[N*4+1];void init(int,int,int);void find(int,int,int,int,int,int);//找被修改区间完全覆盖的点void change(int,int,int,int);inline Ret operator+(const Ret &a,const Ret &b){ return {a.sum+b.sum,(a.sum2+b.sum2)%mo};}Ret query(int,int,int,int,int);int main(){ freopen("seg.in","r",stdin); freopen("seg.out","w",stdout); scanf("%d",&n); for (int i=1;i<=n;++i) scanf("%d",&a[i]); init(1,1,n); int T; scanf("%d",&T); while (T--){ int op; scanf("%d",&op); if (op==1){ int l,r,x; scanf("%d%d%d",&l,&r,&x); find(1,1,n,l,r,x); } else if (op==2){ int l,r; scanf("%d%d",&l,&r); printf("%lld\n",query(1,1,n,l,r).sum); } else{ int l,r; scanf("%d%d",&l,&r); Ret res=query(1,1,n,l,r); printf("%lld\n",(((long long)res.sum2*(r-l+1)%mo+pow2(res.sum%mo)%mo)<<1)%mo); } } return 0;}void init(int k,int l,int r){ if (l==r){ d[k].s.sum=d[k].o=a[l]; d[k].s.sum2=(long long)a[l]*a[l]%mo; return; } int mid=l+r>>1; init(k<<1,l,mid); init(k<<1|1,mid+1,r); d[k].o=d[k<<1].o|d[k<<1|1].o; d[k].s=d[k<<1].s+d[k<<1|1].s;}void find(int k,int l,int r,int st,int en,int x){ if (st<=l && r<=en){ change(k,l,r,x); return; } int mid=l+r>>1; if (st<=mid) find(k<<1,l,mid,st,en,x); if (mid
>1; change(k<<1,l,mid,x); change(k<<1|1,mid+1,r,x); d[k].o=d[k<<1].o|d[k<<1|1].o; d[k].s=d[k<<1].s+d[k<<1|1].s;}Ret query(int k,int l,int r,int st,int en){ if (st<=l && r<=en) return d[k].s; int mid=l+r>>1; Ret res={0,0}; if (st<=mid) res=res+query(k<<1,l,mid,st,en); if (mid

总结

数据结构的时间复杂度,不应该只看他每次操作的复杂度,还要看看总共最多的复杂度。尤其是类似一次性修改的东西(就是这个数据修改过一次之后就不能再修改了)。

话说,我突然想起以前的一道题目:
有一道数据结构提,正解是分块的根号做法,题解说,线段树不能做……
我坚持用线段树,最终AC了那题,log做法,吊打标算。风光了一时
当时用的也差不多是这样的思想……

转载于:https://www.cnblogs.com/jz-597/p/11145262.html

你可能感兴趣的文章
连接Oracle需要jar包和javadoc文档的下载
查看>>
UVA 10976 - Fractions Again?!
查看>>
Dreamweaver cc新版本css单行显示
查看>>
【android】安卓的权限提示及版本相关
查看>>
3D重建的进阶了解---深度图,网格,体素,点云是什么
查看>>
JavaScript可否多线程? 深入理解JavaScript定时机制
查看>>
IOS基础学习
查看>>
PHP 导出 Excell
查看>>
python之-框架
查看>>
Java基础教程——网络基础知识
查看>>
[Web] 如何实现Web服务器和应用服务器的负载均衡?
查看>>
创建文件夹命令
查看>>
自己到底要的是什么
查看>>
this 指向
查看>>
Kruskal基础最小生成树
查看>>
BZOJ.4819.[SDOI2017]新生舞会(01分数规划 费用流SPFA)
查看>>
ubuntu 14.04 安装搜狗拼音输入法
查看>>
c#中 uint--byte[]--char[]--string相互转换汇总
查看>>
浅谈算法和数据结构: 一 栈和队列
查看>>
[WebMatrix] 如何将SQL Compact 4.0 移转至SQL Server 2008 Express
查看>>