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. ACM学习历程—HDU1023 Train Problem II(递推 && 大数)

    Description As we all know the Train Problem I, the boss of the Ignatius Train Station want to know  ...

  2. bzoj 3157 & bzoj 3516 国王奇遇记 —— 推式子

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3157 https://www.lydsy.com/JudgeOnline/problem.p ...

  3. 快速排序的JavaScript实现

    思想 分治的思想,将原始数组分为较小的数组(但没有像归并排序一样将它们分隔开). 主元选择: 从数组中任意选择一项作为主元,通常为数组的第一项,即arr[i]:或数组的中间项, arr[Math.fl ...

  4. python构造一个http请求

    我们经常会用python来进行抓包,模拟登陆等等, 势必要构造http请求包. http的request通常有4个方法get,post,put,delete,分别对应于查询,更新,添加,删除.我们经常 ...

  5. Poj 1936,3302 Subsequence(LCS)

    一.Description(3302) Given a string s of length n, a subsequence of it, is defined as another string ...

  6. kafka之一:Windows上搭建Kafka运行环境

    搭建环境 1. 安装JDK 1.1 安装文件:http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-213315 ...

  7. linux docket

    什么是 Docker Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于  ...

  8. nodejs assert 模块

    assert模块是Node的内置模块,主要用于断定.如果表达式不符合预期,就抛出一个错误.该模块提供11个方法,但只有少数几个是常用的. 1.assert() assert(value[, messa ...

  9. ASE分析

    1.Prepare necessary input files(可以参考上次的博客http://www.cnblogs.com/renping/p/7391028.html) 1)对fq1和fq2合并 ...

  10. centos7命令行和图形界面的相互切换(附centos7安装配置教程)

    一.最近安装了centos7,发现在命令行和图形界面的相互切换命令上,与centos以往版本有很大不同,先整理如下,加深记忆. 1,centos7默认安装后,跟其他版本一样,启动默认进入图形界面: 2 ...