CF 703 F. Pairs of Paths

给出一棵树,询问m条路径,有多少对路径相交于一点。

 

路径相交只有两种情况,一种是相同lca,一种是不同lca。

先对路径[a,b]预处理,记录lca(a,b)下a,b分别在哪个儿子。

对于相同lca的:

 当前查询路径[a,b],则和该路径只在lca相交的路径数等于该lca(a,b)的路径总个数-在a下儿子的个数-在b下儿子的个数+ab下儿子的个数(容斥)。

这里注意的是,如果先统计所有儿子的个数,那么对于单点的路径,他会自己和自己匹配,最后需要减掉。最后结果就是总和/2.

如果是边处理边统计,则不会出现自己和自己匹配,最后结果也不需要除2.

 

对于不同lca的,那么相交必定是一条路径经过另外一条路径的lca。那么对于一个路径[a,b],那么就是统计其他路径的lca在lca(a,b)之上,而且其中一个端点在lca(a,b)的子树中且不在a,b的子树中。

 

因此由于需要统计比lca(a,b)小的路径,这里就有了偏序。对lca根据深度排序。对于lca(a,b),深度比它小的路径已经处理完了。那么和就是在lca(a,b)的子树的节点-在a子树的节点-在b子树的节点。

统计个数可以直接用树状数组。统计子树可以利用dfs序的进和出。求a子树的个数就是sum(out[a]) - sum(in[a]-1) (包括a自己)。

 

 


vector<int> g[N];
int dep[N],fa[N][20],dfn[N],in[N],out[N];
int la[N];
int a[N],b[N],pa[N],pb[N];
int idx;

void dfs(int t, int f){
	dep[t] = dep[f]+1;
	in[t] = idx;
	dfn[idx++]=t;
	fa[t][0] = f;
	for(int i = 1; i <20; ++i){
		fa[t][i] = fa[fa[t][i-1]][i-1];
	}
	for(int u : g[t]){
		if(u==f)continue;
		dfs(u,t);
	}
	out[t] = idx-1;
}

int lca(int a, int b){
	if(dep[a]<dep[b])swap(a,b);
	for(int i = 19;i>=0;--i){
		if(dep[fa[a][i]]>=dep[b]) a = fa[a][i];
	}
	if(a==b) return a;
	for(int i = 19; i >=0;--i){
		if(fa[a][i] != fa[b][i]) {
			a = fa[a][i];
			b = fa[b][i];
		}
	}
	return fa[a][0];
}

int sum[N];
void add(int v){
	for(; v <=idx; v += v&-v){
		sum[v]++;
	}
}

int get1(int v){
	int ret = 0;
	while(v>0){
		ret += sum[v];
		v-=v&-v;
	}
	return ret;
}

int get(int v){
	if(v==-1) return 0;
	return get1(out[v]) - get1(in[v]-1);
}

int get_a(int f, int v){
	if(v==f)return -1;
	for(int i = 19;i >=0;--i){
		if(dep[fa[v][i]] >=dep[f]+1){
			v = fa[v][i];
		}
	}
	return v;
}

int main(){
	int n,m;
	cin>>n;
	fr(i,0,n-1){
		int u,v;
		sf("%d%d",&u,&v);
		g[u].pb(v);
		g[v].pb(u);
	}

	idx = 1;
	dfs(1,1);

	cin>>m;
	vector<int> pos;
	fr(i,0,m){
		sf("%d%d",&a[i],&b[i]);
		if(dep[a[i]] > dep[b[i]]){
			swap(a[i],b[i]);
		}
		else if(dep[a[i]] == dep[b[i]]){
			if(in[a[i]] > in[b[i]]){
				swap(a[i],b[i]);
			}
		}
		la[i] = lca(a[i],b[i]);
		pa[i] = get_a(la[i],a[i]);	
		pb[i] = get_a(la[i],b[i]);	
		pos.pb(i);
	}

	auto cmp = [&](int i, int j){
		return dep[la[i]]<dep[la[j]];
	};
	sort(pos.begin(), pos.end(), cmp);

	int last = 0;
	ll ans = 0;
	for(int i = 0; i < pos.size();++i){
		while(last<pos.size() && dep[la[pos[last]]] < dep[la[pos[i]]]){
			int idx = pos[last++];
			add(in[a[idx]]);
			add(in[b[idx]]);
		}
		int idx = pos[i];
		int tot = get(la[idx]);
		int ask = get(pa[idx]);
		ask += get(pb[idx]);
		ans += tot - ask;
	}

	map<int, vector<pair<int,int> >> p;
	for(int i = 0; i < pos.size();++i){
		int idx = pos[i];
		int pa = get_a(la[idx],a[idx]);	
		int pb = get_a(la[idx],b[idx]);	
		if(pa>pb)swap(pa,pb);
		p[la[idx]].pb(mp(pa,pb));
	}

	ll ans2 = 0;
	for(auto it : p){
		map<int,int> ft;
		map<pair<int,int>,int > ftt;
		auto&vec = it.second;
		ll tot = 0;
		for(auto x : vec) {
			ans2 += tot - ft[x.first] - ft[x.second] + ftt[x];
			if(x.first>=0)
				ft[x.first]++;
			if(x.second>=0)
				ft[x.second]++;
			if(x.first>=0&&x.second>=0)
				ftt[x]++;
			tot++;
		}
	}

	cout<<ans+ans2<<endl;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ava实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),可运行高分资源 Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言中,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言中常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言中常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言中用于封装代码的单元,可以实现代码的复用和模块化。C语言中定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言中用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言中定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言中用于存储同类型数据的结构,可以通过索引访问和修改数组中的元素。字符串是C语言中用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言中用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言中通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值