洛谷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的窗口.现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值. [输入格式:] 输入一共 ...
随机推荐
- bzoj 4034: 树上操作 线段树
题目: 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 ...
- 【QT】对Qt项目开发中遇到的问题的总结
1. QMessageBox中文乱码 这里的中文乱码是指只有QMessageBox才出现中文乱码,其他都可以正常使用的情况.有些博客中提到使用QString::fromUtf8()函数, 实测有些情况 ...
- Poj 1458 Common Subsequence(LCS)
一.Description A subsequence of a given sequence is the given sequence with some elements (possible n ...
- MD5 校验文件
https://blog.csdn.net/wudishine/article/details/42466831 MD5.h #ifndef MD5_H #define MD5_H #include ...
- <正则吃饺子> :关于mybatis中使用的问题(一)
在公司项目开始之前,根据springboot .mybatis.Swagger2 整合了一个demo,在测试时候,遇到的问题,简单记录.之前在使用mybatis时候,没有注意到这一点. 1.错误:Th ...
- Socket对象以及异常
1 socket构造器: public Socket() 创建一个Socket套接字 public Socket(InetAddress address,int port) 创建一个指定IP和端口的 ...
- [hdu4662]MU Puzzle(找规律)
题意:给你一个串MI,按照三种规则1:M后面的部分加倍 2:III->U 3:删去连续的两个UU.看看能否变为给定的串 解题关键:将所有的U转化为I,发现 t+k*6=2^i -> =2^ ...
- 第八篇 elasticsearch链接mysql自动更新数据库
增量更新 input { jdbc { jdbc_driver_library => "D:\tools\mysql\mysql-connector-java-5.1.45/mysql ...
- [原创]Javascript 利用mousetrap.js进行键盘事件操作
我们日常开发中,会遇到js的键盘操作,例如回车提交表单之类的.或者按下某个键执行某个方法.无意中发现一个大小不到4K的js文件,它非常方便的操作键盘事件. 自己也尝试了一下:具体代码如下: 详情可以去 ...
- eclipse 远程操作HIVE
首先启动HiveServer hive --service hiveserver 10000 & 创建工程 引入包: 代码(简单的查询): package com.hive.jdbc; imp ...