一名苦逼的OIer,想成为ACMer

Iowa_Battleship

BZOJ1106 [POI2007]立方体大作战tet

树状数组

原题链接

先说一个结(cai)论(xiang),当两个相同的元素之间有\(x\)个元素是成单的,那么一定要交换\(x\)次。
然而我并不会证(举例子算吗)
然后我们就可以考虑用树状数组来维护两个相同元素之间有多少元素成单。
我们可以直接一个指针扫过去,当遇到一个第一次出现的元素\(x\)时,记录他的位置\(v[x]=i\),并\(add(i,1)\),即在树状数组上对应的位置进行加\(1\);当遇到第二次出现的元素时,将这个元素之前出现的位置执行\(add(v[x],-2)\),即将其在树状数组上变为\(-1\),再将当前位置\(add(i,1)\)。这时我们会发现,在扫到相同元素时,这两个元素之间的区间和就是成单元素的数量。
时间复杂度\(O(nlogn)\)
注意最终结果要除\(2\),因为对于每对不同元素都计算了两次,所以要除去。

#include<cstdio>
using namespace std;
const int N = 1e5 + 10;
int C[N], v[N >> 1], n;
int re()
{
	int x = 0;
	char c = getchar();
	bool p = 0;
	for (; c<'0' || c>'9'; c = getchar())
		p = (c == '-' || p) ? 1 : 0;
	for (; c >= '0'&&c <= '9'; c = getchar())
		x = x * 10 + (c - '0');
	return p ? -x : x;
}
inline int lowbit(int x)
{
	return x & -x;
}
inline void add(int x, int y)
{
	for (; x <= n; x += lowbit(x))
		C[x] += y;
}
inline int ask(int x)
{
	int s = 0;
	for (; x; x -= lowbit(x))
		s += C[x];
	return s;
}
int main()
{
	int i, x, s = 0;
	n = re() << 1;
	for (i = 1; i <= n; i++)
	{
		x = re();
		if (!v[x])
			v[x] = i;
		else
		{
			add(v[x], -2);
			s += ask(i - 1) - ask(v[x]);
		}
		add(i, 1);
	}
	printf("%d", s >> 1);
	return 0;
}

posted on 2018-08-25 19:49  Iowa_Battleship  阅读(167)  评论(0编辑  收藏  举报

导航