P1972 [SDOI2009] HH的项链

这一题一看就感觉可以跟前缀和联系起来,但是问题就是在不断的修改中可能会影响结果。

先是考虑从开始到i的种类和是多少,这时候只需要记录一下每个种类上一次出现的位置,若是出现过,就把之前那个在数组中删掉,在当前位置加上即可,用树状数组就可以动态的求出前缀和。树状数组就是一个板子,比较简单。

问题就是在处理到i + 1的时候再去求前i的种类和是会出现问题的,所以这个时候可以考虑一下离线算法,先把所有的问题输入之后,对问题集以右端点从小到大排序,当处理到i的时候看看是否有右端点为i的问题,这个时候解决这个问题是正确的,每次处理一个数之后就判断是否有以当前位置为右端点的问题即可。

当所有问题解决完之后,按照输入的顺序输出即可,只需要在输入的时候记录一下每个问题的顺序。

P1972 [SDOI2009] HH的项链_第1张图片

#include
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define x first
//#define y second
//#define int long long
using namespace std;
typedef long long ll;
typedef pair pii;
typedef pair pis;
const int mod = 1e9 + 7;
const int N = 1e6+ 10;
int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};
int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};
int n, m;
int o[N];
int last[N];
typedef struct {
	int a, b, i;
}aa;
aa p[N];
bool cmp(aa a, aa b)
{
	return a.b < b.b;
}
int s[N * 4];

int lowbit(int a)
{
	return a & -a;
}

void add(int a, int b)
{
	for(int i = a; i <= n; i += lowbit(i))
		s[i] += b;
	return ;
}

int sum(int a)
{
	int x = 0;
	for(int i = a; i; i -= lowbit(i))
		x += s[i];
	return x;
}

int res[N];
inline void sovle()
{
	cin >> n;
	for(int i = 1; i <= n; i ++)
		cin >> o[i];
	cin >> m;
	for(int i = 0; i < m; i ++)
	{
		cin >> p[i].a >> p[i].b;
		p[i].i = i;
	}
		
	stable_sort(p, p + m, cmp); // 以右端点从小到大排序
	
	int k = 0;
	for(int i = 1; i <= n; i ++)
	{
		if(last[o[i]]){ // 出现过
			add(last[o[i]], -1); // 删除之前的值
			add(i, 1); // 加上当前的值
		}
		else { // 未出现
			add(i, 1); // 加上当前的值
		}
		last[o[i]] = i;
		while(k < m && p[k].b == i) // 解决以当前位置为右端点的所有问题
		{
			res[p[k].i] = sum(p[k].b) - sum(p[k].a - 1);
			k ++;
		}
	}
	for(int i = 0; i < m; i ++) // 按顺序输出所有的问题
	{
		cout << res[i] << endl;
	}
	
}

signed main(void)
{
	IOS;
	int t = 1;
//	cin >> t;
	while(t --) sovle();
	return 0;
}

你可能感兴趣的:(算法)