洛谷P5280 [ZJOI2019]线段树
https://www.luogu.org/problemnew/show/P5280
省选的时候后一半时间开这题,想了接近两个小时的各种假做法,之后想的做法已经接近正解了,但是有一些细节问题理不清楚(事实证明出来后再给我2个小时也还是没理清楚,只能说自己naive),而且也码不完,打了个20分暴力
参考资料:题解
首先,可以分开考虑各个点
对于每个点,考虑对于t次修改,每一次标记为启用或不启用(共有$2^t$种标记方案),其中有多少种标记方案使得这个点最后有tag
询问的答案就是每个点的答案之和
对于每个点:
f[i]表示i点自身有tag的方案数
g[i]表示i或其祖先有tag的方案数(状态里必须是i或其祖先,如果只是祖先(或者只是父亲)就没法做了,以前卡在这个坑里了...)
设这一次是第k次修改
考虑根到某个节点u的链a1,a2,a3,..,at,u
可以发现,这次修改对这个节点的影响分5类:(具体可以参考上面的“参考资料”)
如果"把a1,a2,..,at之一节点直接打tag"(对应参考资料里第4类),则f[u]*=2,g[u]+=$2^{k-1}$
如果"把u直接打tag"(对应第2类),则f[u]+=$2^{k-1}$,g[u]+=$2^{k-1}$
如果"删掉a1,a2,..,at,u的全部tag"(对应第1类),则(啥也没)
如果"删掉a1,a2,..,at的全部tag,如果删掉了至少1个tag就让u加上tag"(对应第3类),则f[u]+=g[u],g[u]*=2
如果"删掉a1,a2,..,ap(p<t)的全部tag(如果删掉了至少1个tag就让a[p+1]加上tag)"(对应第5类),则f[u]*=2,g[u]*=2
这一堆东西是可以用线段树直接维护的,其中第1,2,3类的直接暴力修改,4,5类用懒标记
可以把方案数转换为概率,相当于每次做以上修改时加上的常数都要除以$2^{k-1}$,做完以上修改后再将所有点f和g都除以2,这样可以少记标记,减小常数
这样的话,第4类就是g[u]=g[u]/2+1/2,第5类就是啥也没,剩下几类不需要lazytag比较容易,因此可以只记g的加法和乘法tag
剩下几类:第1类f[u]/=2,g[u]/=2,第2类f[u]=(f[u]+1)/2,g[u]=(g[u]+1)/2,第3类f[u]=(f[u]+g[u])/2
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
const int md=;
#define addto(a,b) ((a)+=(b),((a)>=md)&&((a)-=md))
#define multo(a,b) ((a)=ull(a)*(b)%md)
namespace S
{
struct Nd
{
int f,g,addg,mulg;
int sumf;
}d[];
#define LC (u<<1)
#define RC (u<<1|1)
inline void upd(int l,int r,int u)
{
d[u].sumf=d[u].f;
if(l==r) return;
addto(d[u].sumf,d[LC].sumf);
addto(d[u].sumf,d[RC].sumf);
}
inline void doAddg(int u,int x)
{
addto(d[u].g,x);
addto(d[u].addg,x);
}
inline void doMulg(int u,int x)
{
multo(d[u].g,x);
multo(d[u].addg,x);
multo(d[u].mulg,x);
}
inline void pd(int l,int r,int u)
{
if(l==r) return;
if(d[u].mulg!=)
{
doMulg(LC,d[u].mulg);
doMulg(RC,d[u].mulg);
d[u].mulg=;
}
if(d[u].addg)
{
doAddg(LC,d[u].addg);
doAddg(RC,d[u].addg);
d[u].addg=;
}
}
void build(int l,int r,int u)
{
if(l==r)
{
d[u].mulg=;
return;
}
int mid=(l+r)>>;
build(l,mid,LC);build(mid+,r,RC);
d[u].mulg=;
}
void setx(int L,int R,int l,int r,int u)//X=2^{k-2}
{
pd(l,r,u);
if(L<=l && r<=R)
{
//4类,u的任意后代节点
multo(d[u].addg,);
multo(d[u].mulg,);
addto(d[u].addg,);
//2类,u自身
d[u].f=ull(d[u].f+)*%md;
d[u].g=ull(d[u].g+)*%md;
upd(l,r,u);
return;
}
int mid=(l+r)>>;
if(L<=mid && mid<R)
{
setx(L,R,l,mid,LC);
setx(L,R,mid+,r,RC);
}
else if(L<=mid)
{
setx(L,R,l,mid,LC);
//3类,RC
pd(mid+,r,RC);
d[RC].f=ull(d[RC].f+d[RC].g)*%md;
upd(mid+,r,RC);
//5类,RC的任意后代节点(啥也不干)
}
else if(mid<R)
{
setx(L,R,mid+,r,RC);
//3类,LC
pd(l,mid,LC);
d[LC].f=ull(d[LC].f+d[LC].g)*%md;
upd(l,mid,LC);
//5类,LC的任意后代节点(啥也不干)
}
//1类,u自身
multo(d[u].f,);
multo(d[u].g,);
upd(l,r,u);
}
}
int pw2[];//pw2[i]=2^{i-2}
int n,m,mm;
int main()
{
int i,idx,l,r;
pw2[]=;
for(i=;i<=;++i)
pw2[i]=ull(pw2[i-])*%md;
scanf("%d%d",&n,&m);
S::build(,n,);
while(m--)
{
scanf("%d",&idx);
if(idx==)
{
scanf("%d%d",&l,&r);
++mm;
S::setx(l,r,,n,);
}
else
{
printf("%llu\n",ull(S::d[].sumf)*pw2[mm+]%md);
}
}
return ;
}
洛谷P5280 [ZJOI2019]线段树的更多相关文章
- 洛谷 P5280 - [ZJOI2019]线段树(线段树+dp,神仙题)
题面传送门 神仙 ZJOI,不会做啊不会做/kk Sooke:"这八成是考场上最可做的题",由此可见 ZJOI 之毒瘤. 首先有一个非常显然的转化,就是题目中的"将线段树 ...
- 洛谷P5280 [ZJOI2019]线段树 [线段树,DP]
传送门 无限Orz \(\color{black}S\color{red}{ooke}\)-- 思路 显然我们不能按照题意来每次复制一遍,而多半是在一棵线段树上瞎搞. 然后我们可以从\(modify\ ...
- 洛谷P5280 [ZJOI2019]线段树(线段树)
题面 传送门 题解 考场上就这么一道会做的其它连暴力都没打--活该爆炸-- 首先我们得看出问题的本质:有\(m\)个操作,总共\(2^m\)种情况分别对应每个操作是否执行,求这\(2^m\)棵线段树上 ...
- 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)
To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...
- P5280 [ZJOI2019]线段树
题目链接:洛谷 题目描述:[比较复杂,建议看原题] 这道题太神仙了,线段树上做树形dp. 根据树形dp的套路,都是按照转移的不同情况给节点分类.这里每次modify的时候对于节点的影响也不同,所以我们 ...
- Luogu P5280 [ZJOI2019]线段树
送我退役的神题,但不得不说是ZJOIDay1最可做的一题了 先说一下考场的ZZ想法以及出来后YY的优化版吧 首先发现每次操作其实就是统计出增加的节点个数(原来的不会消失) 所以我们只要统计出线段树上每 ...
- 【洛谷】【线段树】P1471 方差
[题目背景:] 滚粗了的HansBug在收拾旧数学书,然而他发现了什么奇妙的东西. [题目描述:] 蒟蒻HansBug在一本数学书里面发现了一个神奇的数列,包含N个实数.他想算算这个数列的平均数和方差 ...
- 【洛谷】【线段树】P1047 校门外的树
[题目描述:] 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0,1,2,……,L ...
- 【洛谷】【线段树】P1886 滑动窗口
[题目描述:] 现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口.现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值. [输入格式:] 输入一共 ...
随机推荐
- Unity3d中SendMessage 用法
Message相关有3条指令:SendMessage ("函数名",参数,SendMessageOptions) //GameObject自身的ScriptBroadcastM ...
- [转]CSS遮罩——如何在CSS中使用遮罩
特别声明:此篇文章由D姐根据Christian Schaefer的英文文章原名<CSS Masks – How To Use Masking In CSS Now>进行翻译,整个译文带有我 ...
- Linux mount指令
-o,是指option,可以指定username,password:当时我们就碰到一个坎,如何来避免输入用户名密码,其实本质并不是避免输入用户名米吗,而是某种可知的方式来进行权限控制:解决的方式就是采 ...
- sql查询将列里面的值替换为别的值但是实际值不变
数据库有一张表BUG(缺陷记录表) 里面有字段severity(严重程度): severity的值实际为1,2,3,4,但希望在查询结果中将severity的1,2,3,4值显示为其他的值,但seve ...
- C语言学习笔记--enum和sizeof关键字
1.enum关键字 C语言中enum关键字用来定义枚举类型 (1)enum 是 C 语言中的一种自定义类型(2)enum 值是可以根据需要自定义的的整型值(3)第一个定义的 enum 值默认为 0 ( ...
- position应用之相对父元素的定位
分别添加以下style即可: 父元素position:relative; 子元素position:absolute; right:0px; bottom:0px;
- Matlab零碎知识
1.不定积分的求取 int syms x;%为自变量 f=x.^2; s=int(f,x); 其中显示辅助函数simple()和pretty()
- 第三篇elasticsearch分布式安装
elasticSearch 分布式安装 1.在elasticSearch下的config下elasticsearch.yml文件最后一行添加注意 一定要加空格在:后面cluster.name: wal ...
- MySql中的视图的概念及应用
视图的基本概念 视图是从一个或几个基本表(或者视图)导出的表.它与基本表不同,是一个虚表. 数据库只存放视图的定义,而不存放视图对应的数据,这些数据仍存放在原来的基本表中.所以基本表中的数据发生变化, ...
- if if 和 if elif 的区别
再一次编程中意外使用了if if 也实现了 if elif的功能,所以搜索了下其中的区别: 1.if if 和 if elif 是有区别的,只是在某些情况下才会一样的效果: 2.随意使用会导致意外的错 ...