首页 > 其他分享 >洛谷 P10145 [WC/CTS2024] 线段树 题解--zhengjun

洛谷 P10145 [WC/CTS2024] 线段树 题解--zhengjun

时间:2024-02-05 22:33:51浏览次数:30  
标签:rt WC rs -- 题解 sum int ls mul

提供一种考场做法,在思路上和官方题解的差异蛮大的,虽然结果差不多。

首先需要发现 \([l,r)\) 区间可以算出来的充要条件是:

如果对于每个选中的节点 \(u\),连无向边 \((L_u,R_u)\),则当且仅当 \(l\) 和 \(r\) 连通时区间 \([l,r)\) 可以算出来。

证明的话,用前缀和理解这些东西,分别考虑一下充分性和必要性即可,此处不赘述。

接下来,就考虑把这张图先连出来,大概长这样:

image

然后一组边的子集 \(S\) 合法就是对于任意的 \(L_i,R_i\),\(L_i\) 和 \(R_i\) 能够仅通过 \(S\) 中的边连通。

接下来就是精髓了,同时本人做法和官方题解做法的分歧也就在这里。

发现这个东西一点都不优美,很难对 \(m\) 个区间都考虑到,所以考虑转化一下。

具体地,对原图 \(G\) 建立其对偶图 \(G'\),大概长这样(蓝色部分):

image

这样,在 \(G\) 中 \(L,R\) 连通等价于在 \(G'\) 中,存在 \([L,R)\) 区间内的叶子与 \([L,R)\) 区间外的叶子连通(使用了原图中路径和对偶图中的一组割对应的性质)。

虽然看起来更加不简洁,但是我们可以考虑什么样的两对点 \(u,v\) 可以连通。

我们发现,当且仅当覆盖 \([u,u+1)\) 的区间集合与 \([v,v+1)\) 的区间集合相同时,\(u,v\) 可以连通。

这样我们就可以考虑按照覆盖的集合进行染色为 \([1,k]\),\(k\) 为颜色数,第 \(i\) 个叶子的颜色为 \(a_i\)。

那么加上 \(G'\) 是二叉树的良好性质,我们就可以设计 dp 了:

  • \(f_{u,c}\) 表示在 \(u\) 的子树中,和 \(u\) 连通的颜色为 \(c\);
  • \(g_{u}\) 表示在 \(u\) 的子树中,\(u\) 不和任意一个叶子连通;

边界情况:对于 \([0,n)\) 的叶子结点 \(u\),\(f_{u,a_u}=1,g_{u}=0\)。

转移是简单的,具体地:

\[\begin{aligned} g_u =&(2g_{ls_u}+\sum f_{ls_u,c})\times(2g_{rs_u}+\sum f_{rs_u,c})\\ f_{u,c}=&f_{ls_u,c}\times f_{rs_u,c}+\\ &f_{ls_u,c}\times (2g_{rs_u}+\sum f_{rs_u,c})+\\ &f_{rs_u,c}\times (2g_{ls_u}+\sum f_{ls_u,c}) \end{aligned} \]

总之就是讨论 \((u,ls_u),(u,rs_u)\) 是否存在。

然后使用线段树合并就能维护这个东西,时间复杂度 \(\Theta(n\log n)\)。

代码

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using ull=unsigned long long;
#define all(a) (a).begin(),(a).end()
const int N=2e5+10,M=N*2,mod=998244353,K=N*20;
mt19937_64 rnd(time(0));
int n,m,k,ls[M],rs[M],id[M],a[N];
ull c[N];
vector<ull>num;
int build(int l=0,int r=n){
	int rt=++k,mid;
	if(l+1==r)return id[rt]=l,rt;
	id[rt]=-1;
	scanf("%d",&mid);
	ls[rt]=build(l,mid);
	rs[rt]=build(mid,r);
	return rt;
}
namespace SGT{
	struct node{
		int ls,rs,mul,sum;
		node(){ls=rs=sum=0,mul=1;}
	}t[K];
	int k;
	void pushup(int rt){
		t[rt].sum=(t[t[rt].ls].sum+t[t[rt].rs].sum)%mod;
	}
	void pushmul(int rt,int x){
		if(!rt)return;
		t[rt].sum=1ll*t[rt].sum*x%mod,t[rt].mul=1ll*t[rt].mul*x%mod;
	}
	void pushdown(int rt){
		if(t[rt].mul^1){
			pushmul(t[rt].ls,t[rt].mul);
			pushmul(t[rt].rs,t[rt].mul);
			t[rt].mul=1;
		}
	}
	void insert(int &rt,int x,int l=1,int r=num.size()){
		if(!rt)rt=++k;
		if(l==r)return ++t[rt].sum,void();
		int mid=(l+r)>>1;
		pushdown(rt);
		if(x<=mid)insert(t[rt].ls,x,l,mid);
		else insert(t[rt].rs,x,mid+1,r);
		pushup(rt);
	}
	int query(int rt,int x,int l=1,int r=num.size()){
		if(!rt)return 0;
		if(l==r)return t[rt].sum;
		int mid=(l+r)>>1;
		pushdown(rt);
		if(x<=mid)return query(t[rt].ls,x,l,mid);
		else return query(t[rt].rs,x,mid+1,r);
		pushup(rt);
	}
	void merge(int &x,int y,int gl,int gr,int l=1,int r=num.size()){
		if(!x)return x=y,pushmul(x,gl);
		if(!y)return pushmul(x,gr);
		if(l==r){
			t[x].sum=(1ll*t[x].sum*t[y].sum+1ll*t[x].sum*gr+1ll*gl*t[y].sum)%mod;
			return;
		}
		int mid=(l+r)>>1;
		pushdown(x),pushdown(y);
		merge(t[x].ls,t[y].ls,gl,gr,l,mid);
		merge(t[x].rs,t[y].rs,gl,gr,mid+1,r);
		pushup(x);
	}
}
int g[M],root[M];
void dfs(int u){
	if(~id[u]){
		SGT::insert(root[u],a[id[u]]);
	}else{
		dfs(ls[u]),dfs(rs[u]);
		g[u]=1ll*g[ls[u]]*g[rs[u]]%mod;
		SGT::merge(root[u]=root[ls[u]],root[rs[u]],g[ls[u]],g[rs[u]]);
	}
	g[u]=(g[u]*2ll+SGT::t[root[u]].sum)%mod;
}
int main(){
	freopen(".in","r",stdin);
	// freopen(".out","w",stdout);
	scanf("%d%d",&n,&m);
	build();
	for(int l,r;m--;){
		scanf("%d%d",&l,&r);
		ull val=rnd();
		c[l]^=val,c[r]^=val;
	}
	for(int i=0;i<=n;i++)c[i]^=c[i-1];
	num=vector<ull>{c,c+1+n};
	sort(all(num)),num.erase(unique(all(num)),num.end());
	for(int i=0;i<=n;i++)a[i]=lower_bound(all(num),c[i])-num.begin()+1;
	dfs(1);
	cout<<(g[1]+SGT::query(root[1],a[n]))%mod<<endl;
	return 0;
}

标签:rt,WC,rs,--,题解,sum,int,ls,mul
From: https://www.cnblogs.com/A-zjzj/p/18008942

相关文章

  • 题解 CF1849D
    萌新的第一篇题解题目大意对于一个初始颜色都为蓝色的数组\(a_1,a_2,\dots,a_n\)其中\(a_n\in\{0,1,2\}\),现在可以进行两个操作:花费\(1\)个金币,将\(a_i\)涂成红色;选择一个红色的\(a_i>0\),将\(a_{i-1}\)或\(a_{i+1}\)涂成红色,同时\(a_i\)减\(1\)。输出金......
  • RHCE第五周(网络客户端)
    一:浏览网页和下载curl和wget和elinks工具1:curl工具 1:选项-o将要浏览的网页另存为-O将浏览的网页下载-i查看服务信息以及状态码-x远程代理,要加上端口号,服务的安全性,隐藏了原来的端口号2:案例1)查看服务的信息以及状态码状态码:200(能够访问),301表示网址重定......
  • 常用GDB调试命令
    1.启动gdb调试gcc-ghello.c-ohello/gdbhello2.退出调试quit3.给程序设置参数/获取设置参数setargs1020showargs4.查看当前文件代码list行号/函数名(不加则从默认位置显示)5.查看非当前文件代码list文件名:行号/函数名6.设置显示的行数setlist行数7.设......
  • 各类eBPF程序的触发机制及其应用场景
    每一类eBPF程序都有哪些具体的类型,以及这些不同类型的程序都是由哪些事件触发执行的。1、跟踪类eBPF程序跟踪类eBPF程序主要用于从系统中提取跟踪信息,进而为监控、排错、性能优化等提供数据支撑。比如,我们前几讲中的HelloWorld示例就是一个BPF_PROG_TYPE_KPROBE类型的跟......
  • Java测试代码编写
    一、单元测试1.1引入依赖1、root<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><s......
  • 100000行级别数据的 Excel 导入优化之路
    项目中有一个Excel导入的需求:缴费记录导入由实施/用户将别的系统的数据填入我们系统中的Excel模板,应用将文件内容读取、校对、转换之后产生欠费数据、票据、票据详情并存储到数据库中。在接手之前可能由于之前导入的数据量并不多没有对效率有过高的追求。但是到了4.0版本,......
  • 2月摸鱼计划03 从并发编程本质了解Go高性能的本质
    1.0从并发编程本质了解Go高性能的本质1.1Goroutine协程可以理解为轻量级线程;Go更适合高并发场景原因之一:Go语言一次可以创建上万协成;“快速”:开多个协成打印。gofunc():在函数前加go代表创建协程;time.Sleep():协程阻塞,使主协程在子协程结束前阻塞不退出;乱序输出说......
  • 小兔鲜儿 uniapp - uni.request 请求封装 2月摸鱼计划03
    uni.request请求封装添加请求和上传文件拦截器uniapp拦截器:uni.addInterceptor接口说明:接口文档实现步骤基础地址超时时间请求头标识添加token参考代码//src/utils/http.tsconsthttpInterceptor={//拦截前触发invoke(options:UniApp.RequestOptions){//1.......
  • 无涯教程-LOG10E函数
    E的以10为底的对数,约为0.434。LOG10E-语法Math.LOG10ELOG10E-示例console.log(Math.LOG10E)//thebase10logarithmofMath.E:0.434运行上面代码输出0.4342944819032518参考链接https://www.learnfk.com/es6/es6-math-property-log10e......
  • 金融行业多端支付系统强一致性架构设计(下)
    2支付能力的快速接入支付快速接入:设计流程主要目标:屏蔽接入第三方支付平台的复杂度,为业务提供便捷接入的支付的能力。整体交互逻辑:用户下单后,业务线生成生订单的同时请求支付系统,返回携带加密后的收银台链接,业务前端渲染收银台H5链接,之后用户操作都直接与支付系统直接交互,不再经过......