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. SQL中replace函数

    string sql1 = "select price from dbo.eazy_farm where REPLACE(title,' ','')='" + cainame + ...

  2. NetScaler VPX在Azure上的部署(一)

    本文将介绍NetScaler的VPX部署在Azure China上.包括如何通过vhd文件上传.创建虚拟机,以及如何部署VPX. 一.首先将VHD文件解压,放到目录D:\Azure中.VHD文件的获得 ...

  3. 二叉搜索树的结构(30 分) PTA 模拟+字符串处理 二叉搜索树的节点插入和非递归遍历

    二叉搜索树的结构(30 分) 二叉搜索树或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值:若它的右子树不空,则右子树上所有结点的值均大于它的根 ...

  4. 滑动swipe的妙用

    转自:http://www.cnblogs.com/NEOCSL/archive/2013/03/04/2942861.html iterface ITouchable; function OnPic ...

  5. JavaScript之JMap

    在JavaScript中我们利用function类定义类在类的内部我们用var 定义私有变量 私有函数在类的内部我们用this 定义公有变量(1)定义一个类 function JMap() { var ...

  6. springMVC绑定json参数之二(2.2.4)

    二.springmvc 接收不同格式的json字符串 4).格式四:json传递复杂对象(对象中有属性,还有List,这里验证数组接收,不用List,其他的和上一节不变) 测试一: Test对象属性如 ...

  7. commons-configuration读取配置文件

    关键工具类: import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.con ...

  8. CSS多列布局Multi-column、伸缩布局Flexbox、网格布局Grid详解

    新css属性为我们提供了更加便捷的网页布局方式.来自微软的thomas lewis将带你认识去Grid Alignment,Flexibox Box以及Multi-column Layout这三大领域 ...

  9. Learning Python 007 基本语句

    Python 基本语句 if - 条件判断 没有什么好说的,直接上代码: age = 3 if age >= 18: print('adult') elif age >= 6: print ...

  10. contentType和dataType的区别

    contentType: 告诉服务器,我要发什么类型的数据 dataType:告诉服务器,我要想什么类型的数据,如果没有指定,那么会自动推断是返回 XML,还是JSON,还是script,还是Stri ...