bzoj1202(第一篇博客,大家快来捧场吧)

1、一般碰到连续区间的题,可以用并查集,把他的祖先记为能到达的最长区间的端点(通常为右端点);

2、求两个区间的差不一定要硬生生地求,如果有一种比较优的算法能记录这两个区间加上另一个重复的区间,那么减的时候可以抵消掉重复区间,对答案没影响。

3、该题中,f[x]代表以x+1为左端点所在最长区间最右端点,l[x]代表以x+1为左端点所在最长区间的权值(注意是x+1)。记录时做这两步:

①若祖先相同,判断v是否等于l[b]-l[a-1],这很好理解;

②若不同则合并。l[f[a-1]]=l[b]+v-l[[a-1]],f[f[a-1]]=f[b].

这里有一个难点,如果f[a-1]比f[b]大这个式子还成立吗?答案是成立的。因为这时候l[f[a-1]]等于f[b]+1到f[a-1]的值的相反数。当再次查他们的时候,由于fb在左边,所以计算的是l[fb]-l[f[a-1]],这对答案真的没影响!

再深入思考,为什么特例对了呢?我个人认为,l【x】的意义不一定要以左右来看,可以是时间先后,x是第一个点,在这个区间f【x】为他最后到的那个点,往左走当然就是往右走的相反数了!可能没说清,但大家可以感性体会一下这个意思。

下面附AC代码:

#include
using namespace std;
#define maxn 105
int w,n,m,f[maxn],l[maxn];
void find(int x){
	if(x==f[x])return ;
	find(f[x]);
	l[x]+=l[f[x]];
	f[x]=f[f[x]];}
int main(){
	scanf("%d",&w);
	while(w--){
		int j=0;
		scanf("%d%d",&n,&m);
		for(int i=0;i

你可能感兴趣的:(并查集)