ZJOI 2017 树状数组(线段树套线段树)
题意
思路
不难发现,九条カレン醬所写的树状数组,在查询区间 \([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 树状数组(线段树套线段树)的更多相关文章
- 「ZJOI2017」树状数组(二维线段树)
「ZJOI2017」树状数组(二维线段树) 吉老师的题目真是难想... 代码中求的是 \(\sum_{i=l-1}^{r-1}a_i\),而实际求的是 \(\sum_{i=l}^{r}a_i\),所以 ...
- LightOJ 1085(树状数组+离散化+DP,线段树)
All Possible Increasing Subsequences Time Limit:3000MS Memory Limit:65536KB 64bit IO Format: ...
- 敌兵布阵 HDU - 1166 (树状数组模板题,线段树模板题)
思路:就是树状数组的模板题,利用的就是单点更新和区间求和是树状数组的强项时间复杂度为m*log(n) 没想到自己以前把这道题当线段树的单点更新刷了. 树状数组: #include<iostrea ...
- [BZOJ4785][ZJOI2017]树状数组(概率+二维线段树)
4785: [Zjoi2017]树状数组 Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 297 Solved: 195[Submit][Status ...
- BZOJ4785 [Zjoi2017]树状数组 【二维线段树 + 标记永久化】
题目链接 BZOJ4785 题解 肝了一个下午QAQ没写过二维线段树还是很难受 首先题目中的树状数组实际维护的是后缀和,这一点凭分析或经验或手模观察可以得出 在\(\mod 2\)意义下,我们实际求出 ...
- 牛客网 暑期ACM多校训练营(第一场)J.Different Integers-区间两侧不同数字的个数-离线树状数组 or 可持久化线段树(主席树)
J.Different Integers 题意就是给你l,r,问你在区间两侧的[1,l]和[r,n]中,不同数的个数. 两种思路: 1.将数组长度扩大两倍,for(int i=n+1;i<=2* ...
- day 1 堆 hash 线段树 树状数组 冰茶姬 字典树 二叉查找树
来郑州的第二天,早上开始也没说什么就说了些注意安全,各种各样的注意安全... 冰茶姬: 原来再打食物链时看了一下冰茶姬,只注意了路径压缩,没想到还有什么按秩排序但确实快了不少... int find( ...
- P3688 [ZJOI2017] 树状数组 【二维线段树】
题目描述:这里有一个写挂的树状数组: 有两种共\(m\)个操作: 输入\(l,r\),在\([l,r]\)中随机选择一个整数\(x\)执行\(\text{Add}(x)\) 输入\(l,r\),询问执 ...
- 洛谷 P3688 - [ZJOI2017]树状数组(二维线段树+标记永久化)
题面传送门 首先学过树状数组的应该都知道,将树状数组方向写反等价于前缀和 \(\to\) 后缀和,因此题目中伪代码的区间求和实质上是 \(sum[l-1...n]-sum[r...n]=sum[l-1 ...
- HDU 1934 树状数组 也可以用线段树
http://acm.hdu.edu.cn/showproblem.php?pid=1394 或者是我自己挂的专题http://acm.hust.edu.cn/vjudge/contest/view. ...
随机推荐
- numpy交换列
x = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) print(x) x = x[:, [1, 0, 2]] print(x) 输出 [[1 2 3] [4 ...
- bootstrap 自适应和响应式布局的区别
自适应: 不管屏幕多大,都尽量不换行,而只是横向缩放. 响应式: 屏幕变小了之后,属于同一行的元素受到挤压后,行的右边元素自动换行显式: 屏幕大了后,本属于同一行的元素尽可能的排在同一行内: boo ...
- request.getParameterNames()和request.getParameterValues()
request.getParameterNames()方法是将发送请求页面中form表单里所有具有name属性的表单对象获取(包括button).返回一个Enumeration类型的枚举. 通过Enu ...
- jenkins centos slave起不来报错The SSH key presented by the remote host does not match the key saved in the Known Hosts file against this host. Connections to this host will be denied until the two keys mat
场景:我的centos-204是一台centos的机器,本来用https://www.cnblogs.com/zndxall/p/8297356.html 的centos slave方式搭建ok的,一 ...
- python --(链表)
链表的使用 #/usr/bin/python#-*- coding: utf-8 -*-#Function: simulate the link-list in python#__author__: ...
- FB面经Prepare: Bipartite a graph
input friends relations{{1,2}, {2,3}, {3,4}} 把人分成两拨,每拨人互相不认识, 所以应该是group1{1,3}, group2{2,4} 这道题应该是ho ...
- xpath详细讲解
什么是XML XML 指可扩展标记语言(EXtensible Markup Language) XML 是一种标记语言,很类似 HTML XML 的设计宗旨是传输数据,而非显示数据 XML 的标签需要 ...
- CentOS 7 zabbix添加监控服务器
CentOS 7 yum安装zabbix 设置中文界面 安装环境 CentOS 7 关闭防火墙和SElinux 在被监控端安装zabbix-agent [root@zabbix-agent ~]# ...
- Java 动态打印菱形代码之for循环的使用
1.自定义空心菱形 void PrintRhombus() { int i, j; int s = 4; for (i = 1; i < 2 * (s + 1); i++) { if (i &l ...
- Can't find msguniq. Make sure you have GNU gettext tools 0.15 or newer installed
Python Django生成国际化和本地化.po文件步骤1.在settings文件中,添加一下内容: LANGUAGES = ( ('zh-hans', ugettext_lazy('Simplif ...