线段树+dp

B. Hossam and Friends
这个题的大意是输入一些样例,样例中的每一对数是关系不好的人的序号,现在在一个序列中要统计出所有好区段的数目。(好区段是这样定义的:在 [ a , a + 1 , . . , b ] [a,a+1,..,b] [a,a+1,..,b]这个闭区间里面不能有关系不好的人。)
嗯,俺用的方法是线段树,但是应该没有这么麻烦,好像逆序一下就行。我是这样的想的:考虑每一个元素向右可以产生的好区段。比如说:有这样的排序: a , b , c , d , e , f a,b,c,d,e,f a,b,c,d,e,f d d d f f f的关系不好, c c c e e e的关系不好,那么 a a a b b b向右延,最远能到哪呢?很明显,可以到 d d d,也就是现在我得知了一系列的关系不好的朋友对: p i , q i p_{i},q_{i} pi,qi;那么 p i p_{i} pi(含)往左的所有人向右延,都不能到 q i q_{i} qi,也就是要在线段树 [ 1 , p i ] [1,p_{i}] [1,pi]区间上更新成 q i q_{i} qi,而这个线段树维护的是最小值,如果有 p i p_{i} pi的另一个 q i 1 q_{i1} qi1 q i q_{i} qi还小,就起到了作用。
代码:

#include<iostream>
#include<cstdio>
#include<vector>
#define min(a,b)(a<b)?a:b
using namespace std;
typedef long long ll;
void update(int L, int R, int l, int r, int cnt, int v, vector<int> &tree)
{
	if (l > R || r < L)
		return;
	if (l >= L && r <= R)
	{
		tree[cnt] = min(tree[cnt], v);
		return;
	}
	int mid = (l + r) / 2;
	update(L, R, l, mid, cnt * 2, v, tree);
	update(L, R, mid + 1, r, cnt * 2 + 1, v, tree);
	//tree[cnt] = min(tree[cnt * 2], tree[cnt * 2 + 1]);
}
int query(int L, int R, int l, int r, int cnt, vector<int> &tree)
{
	if (l > R || r < L)
		return INT_MAX;
	if (l >= L && r <= R)
	{
		return tree[cnt];
	}
	int mid = (l + r) / 2;
	tree[cnt * 2] = min(tree[cnt * 2], tree[cnt]);
	tree[cnt * 2 + 1] = min(tree[cnt * 2 + 1], tree[cnt]);
	int a = query(L, R, l, mid, cnt * 2, tree);
	int b = query(L, R, mid + 1, r, cnt * 2 + 1, tree);
	return min(a, b);
}
int main(void)
{
	int t;
	scanf_s("%d", &t);
	for (int i = 0; i < t; i++)
	{
		int n, m;
		scanf_s("%d%d", &n, &m);
		if (m == 0)
		{

			printf("%lld\n", (ll)n*(n + 1) / 2);
			continue;
		}
		vector<int> tree(n << 3, n);
		for (int i = 0; i < m; i++)
		{
			int a, b;
			scanf_s("%d%d", &a, &b);
			if (a > b)
			{
				int tmp = b;
				b = a;
				a = tmp;
			}
			update(1, a, 1, n,1, b - 1,tree);
		}
		ll sum = 0;
		int j = 1;
		while(j<=n)
		{
			int yh = query(j, j, 1, n, 1,tree);
			ll q = yh - j + 1;
			sum = sum +q;
			j++;
		}
		printf("%lld\n", sum);
	}
}

另一种方法(能看别人的代码真好)
这个就是把每一对对应的最小值用数组记录下来了,然后逆序走一遍

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
ll i,j,k,n,m,t,a[100500],res;
 
int main(){
	cin>>t;
	while(t--){
		cin>>n>>m;
		for(i=1;i<=n;i++)a[i]=n;
		for(i=1;i<=m;i++){
			cin>>j>>k;
			if(k<j)swap(j,k);
			a[j]=min(a[j],k-1);
		}
		res=n;
		for(i=n-1;i>=1;i--){
			a[i]=min(a[i],a[i+1]);
			res+=a[i]-i;
		}
		cout<<res<<endl;
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值