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]线段树的更多相关文章

  1. 洛谷 P5280 - [ZJOI2019]线段树(线段树+dp,神仙题)

    题面传送门 神仙 ZJOI,不会做啊不会做/kk Sooke:"这八成是考场上最可做的题",由此可见 ZJOI 之毒瘤. 首先有一个非常显然的转化,就是题目中的"将线段树 ...

  2. 洛谷P5280 [ZJOI2019]线段树 [线段树,DP]

    传送门 无限Orz \(\color{black}S\color{red}{ooke}\)-- 思路 显然我们不能按照题意来每次复制一遍,而多半是在一棵线段树上瞎搞. 然后我们可以从\(modify\ ...

  3. 洛谷P5280 [ZJOI2019]线段树(线段树)

    题面 传送门 题解 考场上就这么一道会做的其它连暴力都没打--活该爆炸-- 首先我们得看出问题的本质:有\(m\)个操作,总共\(2^m\)种情况分别对应每个操作是否执行,求这\(2^m\)棵线段树上 ...

  4. 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)

    To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...

  5. P5280 [ZJOI2019]线段树

    题目链接:洛谷 题目描述:[比较复杂,建议看原题] 这道题太神仙了,线段树上做树形dp. 根据树形dp的套路,都是按照转移的不同情况给节点分类.这里每次modify的时候对于节点的影响也不同,所以我们 ...

  6. Luogu P5280 [ZJOI2019]线段树

    送我退役的神题,但不得不说是ZJOIDay1最可做的一题了 先说一下考场的ZZ想法以及出来后YY的优化版吧 首先发现每次操作其实就是统计出增加的节点个数(原来的不会消失) 所以我们只要统计出线段树上每 ...

  7. 【洛谷】【线段树】P1471 方差

    [题目背景:] 滚粗了的HansBug在收拾旧数学书,然而他发现了什么奇妙的东西. [题目描述:] 蒟蒻HansBug在一本数学书里面发现了一个神奇的数列,包含N个实数.他想算算这个数列的平均数和方差 ...

  8. 【洛谷】【线段树】P1047 校门外的树

    [题目描述:] 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0,1,2,……,L ...

  9. 【洛谷】【线段树】P1886 滑动窗口

    [题目描述:] 现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口.现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值. [输入格式:] 输入一共 ...

随机推荐

  1. Unity3d中SendMessage 用法

      Message相关有3条指令:SendMessage ("函数名",参数,SendMessageOptions) //GameObject自身的ScriptBroadcastM ...

  2. [转]CSS遮罩——如何在CSS中使用遮罩

    特别声明:此篇文章由D姐根据Christian Schaefer的英文文章原名<CSS Masks – How To Use Masking In CSS Now>进行翻译,整个译文带有我 ...

  3. Linux mount指令

    -o,是指option,可以指定username,password:当时我们就碰到一个坎,如何来避免输入用户名密码,其实本质并不是避免输入用户名米吗,而是某种可知的方式来进行权限控制:解决的方式就是采 ...

  4. sql查询将列里面的值替换为别的值但是实际值不变

    数据库有一张表BUG(缺陷记录表) 里面有字段severity(严重程度): severity的值实际为1,2,3,4,但希望在查询结果中将severity的1,2,3,4值显示为其他的值,但seve ...

  5. C语言学习笔记--enum和sizeof关键字

    1.enum关键字 C语言中enum关键字用来定义枚举类型 (1)enum 是 C 语言中的一种自定义类型(2)enum 值是可以根据需要自定义的的整型值(3)第一个定义的 enum 值默认为 0 ( ...

  6. position应用之相对父元素的定位

    分别添加以下style即可: 父元素position:relative; 子元素position:absolute; right:0px; bottom:0px;

  7. Matlab零碎知识

    1.不定积分的求取 int syms x;%为自变量 f=x.^2; s=int(f,x); 其中显示辅助函数simple()和pretty()

  8. 第三篇elasticsearch分布式安装

    elasticSearch 分布式安装 1.在elasticSearch下的config下elasticsearch.yml文件最后一行添加注意 一定要加空格在:后面cluster.name: wal ...

  9. MySql中的视图的概念及应用

    视图的基本概念 视图是从一个或几个基本表(或者视图)导出的表.它与基本表不同,是一个虚表. 数据库只存放视图的定义,而不存放视图对应的数据,这些数据仍存放在原来的基本表中.所以基本表中的数据发生变化, ...

  10. if if 和 if elif 的区别

    再一次编程中意外使用了if if 也实现了 if elif的功能,所以搜索了下其中的区别: 1.if if 和 if elif 是有区别的,只是在某些情况下才会一样的效果: 2.随意使用会导致意外的错 ...