[ZJOI2019] 线段树

一、题目

点此看题

二、解法

考虑修改操作对每个点的影响,我们可以按照与修改区间的关系把点分类(相交,包含,相离),在此基础上,包含或相离节点的状态还与其父亲有关系,所以它们还要按照父亲的状态再次分类。

具体可以分为一下五类:

  • 与修改区间相交,但不包含在修改区间内部的节点。
  • 包含在修改区间内部,但其父亲不存在或者不包含在修改区间内部。
  • 与修改区间相离,但是其父亲与修改区间相交。
  • 包含在修改区间内部,其父亲也包含在修改区间内部。
  • 与修改区间相离,其父亲也与修改区间相离。

分析影响:

  • 对于第一类节点,操作后无标记。
  • 对于第二类节点,操作后有标记。
  • 对于第三类节点,有无标记取决于这个节点到根的链上是否有标记。
  • 对于第四类和第五类节点,操作无影响。

\(f[u]\) 表示 \(u\) 有标记的占比,\(g[u]\) 表示 \(u\) 的链上有标记的占比:

  • 对于第一类节点,一半保持原样,另一半无标记,\((f[u],g[u])=(\frac{1}{2}f[u],\frac{1}{2}g[u])\)
  • 对于第二类节点,一半保持原样,另一半有标记,\((f[u],g[u])=(\frac{1}{2}f[u]+\frac{1}{2},\frac{1}{2}g[u]+\frac{1}{2})\)
  • 对于第三类节点,一半保持原样,另一半取决于 \(u\) 到根的路径,\((f[u],g[u])=(\frac{1}{2}f[u]+\frac{1}{2}g[u],g[u])\)
  • 对于第四类节点,一半保持原样,另一半点 \(u\) 不受影响,但是到根路径一定有标记:\((f[u],g[u])=(f[u],\frac{1}{2}g[u]+\frac{1}{2})\)
  • 对于第五类节点,一半保持原样,另一半也不受影响:\((f[u],g[u])=(f[u],g[u])\)

前三类的节点数都是 \(O(\log n)\) 的,后两类的节点数是 \(O(n)\) 的,所以必须以打标记的方式维护第四类节点。

时间复杂度 \(O(n\log n)\)

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 400005;
#define int long long
const int MOD = 998244353;
const int inv = (MOD+1)/2;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,p,fl[M],f[M],g[M],s[M];
void work(int i,int c)
{
    g[i]=(g[i]*c+1-c+MOD)%MOD;
    fl[i]=fl[i]*c%MOD;
}
void upd(int i,int ty)
{
    s[i]=f[i];
    if(!ty) s[i]=(s[i]+s[i<<1]+s[i<<1|1])%MOD;
}
void down(int i)
{
    if(fl[i]==1) return ;
    work(i<<1,fl[i]);work(i<<1|1,fl[i]);
    fl[i]=1;
}
void dfs(int i,int l,int r,int L,int R)
{
    if(L>r || l>R)//3
    {
        f[i]=(f[i]+g[i])*inv%MOD;
        upd(i,l==r);return ;
    }
    if(L<=l && r<=R)//2
    {
        f[i]=(f[i]+1)*inv%MOD;
        upd(i,l==r);
        work(i,inv);//2&4
        return ;
    }
    int mid=(l+r)>>1;down(i);
    f[i]=f[i]*inv%MOD;//1
    g[i]=g[i]*inv%MOD;
    dfs(i<<1,l,mid,L,R);
    dfs(i<<1|1,mid+1,r,L,R);
    upd(i,0);
}
signed main()
{
    n=read();m=read();p=1;
    for(int i=1;i<=4*n;i++) fl[i]=1;
    while(m--)
    {
        int op=read();
        if(op==1)
        {
            int l=read(),r=read();
            dfs(1,1,n,l,r);
            p=(p+p)%MOD;
        }
        else printf("%lld\n",p*s[1]%MOD);
    }
}
posted @ 2022-07-20 20:12  C202044zxy  阅读(123)  评论(2编辑  收藏  举报