概率+树套树——UOJ#291/Luogu3688 [ZJOI2017]树状数组

题面:Luogu3688 UOJ#291
我考场上这题打都没打QAQ,出来说这题是Day1最水的一道。。。
题目的大意嘛。。。就是这张图啦(Ps:orz SW_Wind)

(注意到可怜手上拿着的倒着的“树状数组”了吗


接下来开始了无限的懵逼。。。
过了N多个月,看了N多个题解之后,我终于懵逼地在luogu上卡着AC了
然而UOJ还是被卡常90分QAQ
首先要知道的是可怜写的sb“树状数组”求的是后缀和
于是对于某个区间 [l,r] ,这个“树状数组”询问到的是 [l1,r1]
唯一的区别就在于两个节点: l1 r
还有因为在模2域下,所以我们只要关心两个节点同时被修改奇数次或者偶数次的概率就行了
所以我们把这两个点看成二元组,然后构建一棵树套树,外层左端点,内层右端点
修改操作的时候有几种情况:
1. 查询左端点在修改区间外,右端点在内,右端点的修改概率为 1yx+1
2. 查询右端点在修改区间外,左端点在内,左端点的修改概率也是 1yx+1
3. 查询两端点都在区间内,修改概率为 2yx+1
4. 都不在,不用考虑(就是没有)

还有要特判 l=1 的情况,这种情况可怜的代码也有特判,因此只要关心右端点的情况就好了,修改同上

最后,我的程序是乱打的,然后莫名其妙在洛谷上AC了。。。

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <ctime>
#include <map>
#include <queue>
#include <cstdlib>
#include <string>
#include <climits>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
const ll MOD=998244353;
ll ans=0;
int root[400010],ls[40000010],rs[40000010],t[40000010],cnt=0,n,m;
inline ll mi(ll a,ll b){
    ll x=1,y=a;while(b){if(b&1)x=x*y%MOD;y=y*y%MOD;b>>=1;}return x;
}
inline ll cal(ll x,ll y){return (x+y-x*y*2%MOD+MOD)%MOD;}//合并概率
inline void xg(int l,int r,int i,int j,int v,int& nod){//内层区间修改
    if(!nod)nod=++cnt,t[nod]=0;
    if(l>=i&&r<=j){t[nod]=cal(t[nod],v);return;}
    int mid=l+r>>1;
    if(i<=mid)xg(l,mid,i,j,v,ls[nod]);
    if(j>mid)xg(mid+1,r,i,j,v,rs[nod]);
}
inline void Xg(int l,int r,int i1,int j1,int i2,int j2,int v,int nod){//外层区间修改
    if(l>=i1&&r<=j1){xg(0,n,i2,j2,v,root[nod]);return;}
    int mid=l+r>>1;
    if(i1<=mid)Xg(l,mid,i1,j1,i2,j2,v,nod*2);
    if(j1>mid)Xg(mid+1,r,i1,j1,i2,j2,v,nod*2+1);
}
inline void ssum(int l,int r,int i,int nod){//内层查询
    if(!nod)return;
    ans=cal(ans,t[nod]);if(l==r)return;//一直加的原因是区间修改的时候没有下传,和标记永久化有点像。。。
    int mid=l+r>>1;
    if(i<=mid)ssum(l,mid,i,ls[nod]);else ssum(mid+1,r,i,rs[nod]);
}
inline void Ssum(int l,int r,int i,int j,int nod){//外层查询
    ssum(0,n,j,root[nod]);//同上
    if(l==r)return;
    int mid=l+r>>1;
    if(i<=mid)Ssum(l,mid,i,j,nod*2);else Ssum(mid+1,r,i,j,nod*2+1);
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m;
    while(m--){
        int p,x,y;cin>>p>>x>>y;
        if(p==1){
            ll k=mi(y-x+1,MOD-2);
            if(x>1)Xg(0,n,1,x-1,x,y,k,1);
            if(y<n)Xg(0,n,x,y,y+1,n,k,1);
            Xg(0,n,x,y,x,y,k*2ll%MOD,1);//对应的3种情况
            Xg(0,n,0,0,0,x-1,1,1);
            if(y<n)Xg(0,n,0,0,y+1,n,1,1);
            Xg(0,n,0,0,x,y,k*(ll)(y-x)%MOD,1);//特判1
        }else{
            ans=1;Ssum(0,n,x-1,y,1);
            cout<<ans<<endl;
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值