题意

http://uoj.ac/problem/291

思路

不难发现,九条カレン醬所写的树状数组,在查询区间 \([1,r]\) 的时候,其实在查询后缀 \([r,n]\) ;在查询 \([l,r](l\neq1)\) 的时候,则是在查询 \([l-1,r-1]\) 。那么在查询 \([1,r]\) 的时候,只需要询问 \(r\) 的前后缀异或是否相等;在查询 \([l,r](l\neq 1)\) 的时候,只需要询问 \(a[l-1],a[r]\) 是否相等。

考虑 \(O(n^2)\) 的暴力。我们把询问分成上述的两类。第一类询问如果修改到了点 \(r\) ,则无影响,否则就是相等变不相等的转化,分询问区间盖住 \(r\) 和不盖住 \(r\) 两种情况考虑。设原来相等的概率为 \(p\) ,再进行修改不影响的概率为 \(q\) ,那么修改后相等的概率就是 \(pq+(1-p)(1-q)\) 。对于第二类询问也是一样的,分区间覆盖 \(l-1\) 点和 \(r\) 点、覆盖其中一个点、都不覆盖三种情况考虑。代码中有切了这一档分,方便和正解对照。

我们可以同时维护住所有答案,然后只接回答询问。用一个一维数据结构维护每个点 \(x\) 的前缀或者后缀是否相等,一个二维数据结构用来维护 \(a[x],a[y]\) 的值是否相等。修改和上面的暴力是同理的,是对一个区间(一维或二维)的点附上一个修改后相等的概率,对于修改显然是交换结合都没什么关系。那这个一维数据结构选择线段树,二维数据结构选择线段树套线段树即可。

二维线段树比较好写的写法是静点套动点,不过动点套动点也可以写的。而且这道题其实空间是不够的,但比较难卡,一般卡不满。

代码

#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
template<typename T,typename _T>inline bool chk_min(T &x,const _T y){return y<x?x=y,1:0;}
template<typename T,typename _T>inline bool chk_max(T &x,const _T y){return x<y?x=y,1:0;}
typedef long long ll;
const int P=998244353;
const int N=1e5+5;
int op[N],ql[N],qr[N];
int n,m;
void exgcd(int a,int b,int &x,int &y)
{
if(!b){x=1,y=0;return;}
exgcd(b,a%b,y,x),y-=a/b*x;
}
int inv(int a)
{
int x,y;
exgcd(a,P,x,y);
return (x%P+P)%P;
}
namespace Subtask1
{
int merge(int x,int y)
{
return ((1ll*x*y+1ll*(1-x)*(1-y))%P+P)%P;
}
void Solve()
{
FOR(i,1,m)if(op[i]==2)
{
int l=ql[i],r=qr[i];
int p=1;
if(l==1)
{
FOR(j,1,i-1)if(op[j]==1)
{
int len=qr[j]-ql[j]+1;
if(ql[j]<=r&&r<=qr[j])
p=merge(p,inv(len));
else p=merge(p,0);
}
}
else
{
l--;
FOR(j,1,i-1)if(op[j]==1)
{
int len=qr[j]-ql[j]+1;
if(ql[j]<=l&&r<=qr[j])
p=merge(p,1ll*(len-2)*inv(len)%P);
else if((ql[j]<=l&&l<=qr[j])||(ql[j]<=r&&r<=qr[j]))
p=merge(p,1ll*(len-1)*inv(len)%P);
}
}
printf("%d\n",p);
}
}
}; namespace Subtask2
{
int merge(int x,int y)
{
return ((1ll*x*y+1ll*(1-x)*(1-y))%P+P)%P;
}
struct SegmentTree
{
int pw[N<<2];
void build(int k,int l,int r)
{
pw[k]=1;
if(l==r)return;
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
void update(int k,int L,int R,int val,int l,int r)
{
if(L<=l&&r<=R)
{
pw[k]=merge(pw[k],val);
return;
}
int mid=(l+r)>>1;
if(L<=mid)update(k<<1,L,R,val,l,mid);
if(R>mid)update(k<<1|1,L,R,val,mid+1,r);
}
int query(int k,int x,int l,int r)
{
if(l==r)return pw[k];
int mid=(l+r)>>1;
if(x<=mid)return merge(pw[k],query(k<<1,x,l,mid));
else return merge(pw[k],query(k<<1|1,x,mid+1,r));
}
};
struct SegmentTree2D
{
int lson[N*450],rson[N*450],pw[N*450];
int rt[N<<2],tot;
void build()
{
memset(rt,0,sizeof(rt));
tot=0;
}
void create(int &k)
{
k=++tot;
lson[k]=rson[k]=0;
pw[k]=1;
}
void update(int &k,int L,int R,int val,int l,int r)
{
if(!k)create(k);
if(L<=l&&r<=R)
{
pw[k]=merge(pw[k],val);
return;
}
int mid=(l+r)>>1;
if(L<=mid)update(lson[k],L,R,val,l,mid);
if(R>mid)update(rson[k],L,R,val,mid+1,r);
}
int query(int k,int x,int l,int r)
{
if(!k)return 1;
if(l==r)return pw[k];
int mid=(l+r)>>1;
if(x<=mid)return merge(pw[k],query(lson[k],x,l,mid));
else return merge(pw[k],query(rson[k],x,mid+1,r));
}
void Update(int k,int U,int D,int L,int R,int val,int u,int d,int l,int r)
{
if(U<=u&&d<=D)
{
update(rt[k],L,R,val,l,r);
return;
}
int mid=(u+d)>>1;
if(U<=mid)Update(k<<1,U,D,L,R,val,u,mid,l,r);
if(D>mid)Update(k<<1|1,U,D,L,R,val,mid+1,d,l,r);
}
int Query(int k,int x,int y,int u,int d,int l,int r)
{
if(u==d)return query(rt[k],y,l,r);
int mid=(u+d)>>1;
if(x<=mid)return merge(query(rt[k],y,l,r),Query(k<<1,x,y,u,mid,l,r));
else return merge(query(rt[k],y,l,r),Query(k<<1|1,x,y,mid+1,d,l,r));
}
};
SegmentTree ST;
SegmentTree2D ST2;
void Solve()
{
ST.build(1,1,n);
ST2.build();
FOR(i,1,m)
{
if(op[i]==1)
{
int len=qr[i]-ql[i]+1;
ST.update(1,ql[i],qr[i],inv(len),1,n);
if(ql[i]>1)ST.update(1,1,ql[i]-1,0,1,n);
if(qr[i]<n)ST.update(1,qr[i]+1,n,0,1,n);
ST2.Update(1,ql[i],qr[i],ql[i],qr[i],1ll*(len-2)*inv(len)%P,1,n,1,n);
if(ql[i]>1)ST2.Update(1,1,ql[i]-1,ql[i],qr[i],1ll*(len-1)*inv(len)%P,1,n,1,n);
if(qr[i]<n)ST2.Update(1,ql[i],qr[i],qr[i]+1,n,1ll*(len-1)*inv(len)%P,1,n,1,n);
}
else if(op[i]==2)
{
if(ql[i]==1)printf("%d\n",ST.query(1,qr[i],1,n));
else printf("%d\n",ST2.Query(1,ql[i]-1,qr[i],1,n,1,n));
}
}
}
}; int main()
{
scanf("%d%d",&n,&m);
FOR(i,1,m)scanf("%d%d%d",&op[i],&ql[i],&qr[i]);
if(n<=3000&&m<=3000)
{
Subtask1::Solve();
return 0;
}
Subtask2::Solve();
return 0;
}

ZJOI 2017 树状数组(线段树套线段树)的更多相关文章

  1. 「ZJOI2017」树状数组(二维线段树)

    「ZJOI2017」树状数组(二维线段树) 吉老师的题目真是难想... 代码中求的是 \(\sum_{i=l-1}^{r-1}a_i\),而实际求的是 \(\sum_{i=l}^{r}a_i\),所以 ...

  2. LightOJ 1085(树状数组+离散化+DP,线段树)

    All Possible Increasing Subsequences Time Limit:3000MS     Memory Limit:65536KB     64bit IO Format: ...

  3. 敌兵布阵 HDU - 1166 (树状数组模板题,线段树模板题)

    思路:就是树状数组的模板题,利用的就是单点更新和区间求和是树状数组的强项时间复杂度为m*log(n) 没想到自己以前把这道题当线段树的单点更新刷了. 树状数组: #include<iostrea ...

  4. [BZOJ4785][ZJOI2017]树状数组(概率+二维线段树)

    4785: [Zjoi2017]树状数组 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 297  Solved: 195[Submit][Status ...

  5. BZOJ4785 [Zjoi2017]树状数组 【二维线段树 + 标记永久化】

    题目链接 BZOJ4785 题解 肝了一个下午QAQ没写过二维线段树还是很难受 首先题目中的树状数组实际维护的是后缀和,这一点凭分析或经验或手模观察可以得出 在\(\mod 2\)意义下,我们实际求出 ...

  6. 牛客网 暑期ACM多校训练营(第一场)J.Different Integers-区间两侧不同数字的个数-离线树状数组 or 可持久化线段树(主席树)

    J.Different Integers 题意就是给你l,r,问你在区间两侧的[1,l]和[r,n]中,不同数的个数. 两种思路: 1.将数组长度扩大两倍,for(int i=n+1;i<=2* ...

  7. day 1 堆 hash 线段树 树状数组 冰茶姬 字典树 二叉查找树

    来郑州的第二天,早上开始也没说什么就说了些注意安全,各种各样的注意安全... 冰茶姬: 原来再打食物链时看了一下冰茶姬,只注意了路径压缩,没想到还有什么按秩排序但确实快了不少... int find( ...

  8. P3688 [ZJOI2017] 树状数组 【二维线段树】

    题目描述:这里有一个写挂的树状数组: 有两种共\(m\)个操作: 输入\(l,r\),在\([l,r]\)中随机选择一个整数\(x\)执行\(\text{Add}(x)\) 输入\(l,r\),询问执 ...

  9. 洛谷 P3688 - [ZJOI2017]树状数组(二维线段树+标记永久化)

    题面传送门 首先学过树状数组的应该都知道,将树状数组方向写反等价于前缀和 \(\to\) 后缀和,因此题目中伪代码的区间求和实质上是 \(sum[l-1...n]-sum[r...n]=sum[l-1 ...

  10. HDU 1934 树状数组 也可以用线段树

    http://acm.hdu.edu.cn/showproblem.php?pid=1394 或者是我自己挂的专题http://acm.hust.edu.cn/vjudge/contest/view. ...

随机推荐

  1. 本地浏览器Websql数据库操作

    前几天看到一个小姐姐问我一个添加修改的我看了一下案例弄了一个出来.... 参考案例:HTML5本地数据库(WebSQL)[转] - 狂流 - 博客园  https://www.cnblogs.com/ ...

  2. sudo: java 找不到命令

    解决方法: :~$ sudo visudo 在secure_path后加上JDK工具的路径,如: :/jdk安装路径/jdk1..0_144/bin 之后就可以使用"sudo java *. ...

  3. VUE-003-前端表格数据展示时,设置单元格(el-table-column)保留空格和换行

    在使用 el-table 展示数据时,单元格中的数据有可能存在空格和换行符,若不进行设置,浏览器默认会取消空格和换行符,如下所示: 解决方法: 将单元格的样式 “white-space” 属性设置为“ ...

  4. extundelete数据恢复

    需要安装的依赖包: 1. e2fsprogs软件包已安装2. e2fsprogs-libs软件包已安装3. e2fsprogs-devel软件包已安装4. gcc软件包已安装5. gcc-c++ 软件 ...

  5. theano使用GPU踩坑

    1.安装pygpu的部分 #使用豆瓣源or不使用,均安装失败 pip install pygpu -i http://pypi.douban.com/simple/ --trusted-host py ...

  6. React表格报错Each record in table should have a unique `key` prop,or set `rowKey` to an unique primary key.

    解决: <Table bordered rowKey={record=>record.id} //解决 components={this.components} columns={colu ...

  7. 上海嘉韦思杯部分writeup

    第二题 打开赛题,看到加密字符串,进行base64解密,发现是JSFUCK,再次解密,控制台得到flag. 第三题 打开频谱图得到flag,flag中有三个_,联想到音频文件详细信息中的三个zero, ...

  8. https----------如何在phpstudy环境下配置apache的https访问以及访问http自动跳转成https

    1.首先在 httpd.conf里面修改几个地方 找到 #LoadModule ssl_module modules/mod_ssl.so 去掉前面的# Include conf/vhosts.con ...

  9. 再谈javascriptjs原型与原型链及继承相关问题

    什么是原型语言 只有对象,没有类;对象继承对象,而不是类继承类. “原型对象”是核心概念.原型对象是新对象的模板,它将自身的属性共享给新对象.一个对象不但可以享有自己创建时和运行时定义的属性,而且可以 ...

  10. day20 python常用模块

    认识模块 什么是模块? 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀.     但其实import加载的模块分为四个通用类别: 1 使用pytho ...