1 条题解

  • 0
    @ 2025-8-24 22:40:23

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar CaiZi
    初三蒟蒻 || 坐标 FJ || ENFP-A || MC&PVZ&RS 玩家 || 出题团为 CZOI(80765)

    搬运于2025-08-24 22:40:23,当前版本为作者最后更新于2025-04-05 16:30:19,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    首先序列长度不是 22 的次幂非常难受,考虑把它补成 22 的次幂,多余的位置 si=1s_i=1。然后考虑把这些东西插入 01-trie。以下记 depidep_i 为节点 ii 距离最近叶子节点的距离(不是传统的定义),那么点 ii 的子树的叶子个数为 2depi2^{dep_i},若点 ii 这一位为 11,则贡献为 2depi12^{dep_i-1}

    然后我们要知道,01-trie 和线段树是等价的。对于点 ii 的子树的叶子,其在从高往低数前 ii 位是相同的,这启发我们在线段树上将其拆成 logn\log n 个长度为 2l2^l 的区间处理,然后分别处理。

    我们先考虑从点 11 到点 ii 这一段拼接而成的数的最小贡献,直接在 01-trie 上贪心,能走数字相同的点就走,否则若当前在点 jj,会有 2depi×2depj12^{dep_i}\times2^{dep_j-1} 的贡献(前面为贡献的点的个数,后面为单个点贡献的值)。

    我们再考虑点 ii 的子树的最小贡献,首先高位尽量相同肯定是更优的,因此不能破坏前一步走的路径,设当前在点 jj,我们本质上要求的就是点 jj 的子树的最小贡献,设为 valjval_j,考虑标记合并。记 lc,rclc,rc 分别为左右儿子。

    • 如果左右子树都可以走,则左右子树中的叶子均没有任何贡献,valjval_jvallc+valrcval_{lc}+val_{rc}
    • 如果只有左子树可以走,因为右子树除了右儿子以外的其他部分和左子树是一样的,同时右子树的所有叶子都需要额外加上右儿子的贡献,valjval_j2vallc+2depj1×2depj12val_{lc}+2^{dep_j-1}\times2^{dep_j-1}。只有右子树可以走的情况同理。
    • 否则,valjval_j1-1,表示点 jj 的子树不可以走。

    修改操作直接上线段树打懒标记,如果点 ii 对应的区间全被修改为 00valival_i 改为 1-1,否则 valival_i 改为 00

    时间复杂度 O(nlogn+mlog2n)O(n\log n+m\log^2n)

    01-trie 的题直接讲都非常抽象,建议配合代码理解。代码展示:

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    int v,n,m,k,dep[524288],val[524288],tag[524288];
    inline void pushup(int p){
    	if(val[p<<1]!=-1&&val[p<<1|1]!=-1){
    		val[p]=val[p<<1]+val[p<<1|1];
    	}
    	else if(val[p<<1]!=-1){
    		val[p]=(val[p<<1]<<1)+((int)(1)<<(dep[p]<<1)-2);
    	}
    	else if(val[p<<1|1]!=-1){
    		val[p]=(val[p<<1|1]<<1)+((int)(1)<<(dep[p]<<1)-2);
    	}
    	else{
    		val[p]=-1;
    	}
    }
    inline void pushdown(int p){
    	if(tag[p]!=-1){
    		if(tag[p]){
    			val[p<<1]=val[p<<1|1]=0;
    		}
    		else{
    			val[p<<1]=val[p<<1|1]=-1;
    		}
    		tag[p<<1]=tag[p<<1|1]=tag[p];
    		tag[p]=-1;
    	}
    }
    inline void build(int l,int r,int p,int t){
    	tag[p]=-1;
    	dep[p]=t;
    	if(l==r){
    		if(l>n){
    			val[p]=-1;
    		}
    		return;
    	}
    	build(l,l+r>>1,p<<1,t-1);
    	build((l+r>>1)+1,r,p<<1|1,t-1);
    	pushup(p);
    }
    inline void update(int l,int r,int p,int a,int b,int c){
    	if(a<=l&&r<=b){
    		tag[p]=c;
    		if(c){
    			val[p]=0;
    		}
    		else{
    			val[p]=-1;
    		}
    		return;
    	}
    	pushdown(p);
    	if(a<=(l+r>>1)){
    		update(l,l+r>>1,p<<1,a,b,c);
    	}
    	if(b>(l+r>>1)){
    		update((l+r>>1)+1,r,p<<1|1,a,b,c);
    	}
    	pushup(p);
    }
    inline int find(int p,int t,int s){
    	if(dep[p]==t){
    		return val[p];
    	}
    	pushdown(p);
    	if(val[(p<<1)|(s>>dep[p]-1&1)]!=-1){
    		return find((p<<1)|(s>>dep[p]-1&1),t,s);
    	}
    	else{
    		return find((p<<1)|(s>>dep[p]-1&1^1),t,s)+((int)(1)<<dep[p]+t-1);
    	}
    }
    inline int query(int l,int r,int p,int a,int b,int s){
    	if(a<=l&&r<=b){
    		return find(1,dep[p],s);
    	}
    	int c=0;
    	pushdown(p);
    	if(a<=(l+r>>1)){
    		c+=query(l,l+r>>1,p<<1,a,b,s);
    	}
    	if(b>(l+r>>1)){
    		c+=query((l+r>>1)+1,r,p<<1|1,a,b,s+(1<<dep[p]-1));
    	}
    	return c;
    }
    signed main(){
    	int p,a,b;
    	cin.tie(nullptr)->sync_with_stdio(0);
    	cin>>v>>m>>n;
    	for(;(1<<k)<=n;k++);
    	build(0,(1<<k)-1,1,k);
    	while(v--){
    		cin>>a>>b;
    		update(0,(1<<k)-1,1,a,b,0);
    	}
    	while(m--){
    		cin>>p>>a>>b;
    		if(p==0){
    			if(val[1]==-1){
    				cout<<"Death\n";
    			}
    			else{
    				cout<<query(0,(1<<k)-1,1,a,b,0)<<'\n';
    			}
    		}
    		else{
    			update(0,(1<<k)-1,1,a,b,p-1);
    		}
    	}
    	return 0;
    }
    
    • 1

    信息

    ID
    7645
    时间
    1000ms
    内存
    128MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者