BZOJ1106[POI2007]立方体大作战tet - 树状数组
描述
一个叫做立方体大作战的游戏风靡整个Byteotia。这个游戏的规则是相当复杂的,所以我们只介绍他的简单规
则:给定玩家一个有2n个元素的栈,元素一个叠一个地放置。这些元素拥有n个不同的编号,每个编号正好有两个
元素。玩家每次可以交换两个相邻的元素。如果在交换之后,两个相邻的元素编号相同,则将他们都从栈中移除,
所有在他们上面的元素都会掉落下来并且可以导致连锁反应。玩家的目标是用最少的步数将方块全部消除。
题解
我们首先能大胆猜测,如果两个相同元素中间有一个在只出现一次的元素, 那么需要交换一次。
接下来我们只要求出每种元素的区间内有几个只出现一次的元素。
一次扫过$2 * n$个元素, 如果这种元素在之前没有出现过, 那么把该位置值记为 $1$
如果这种元素已经出现过, 那么把之前出现过的位置值记为-1, 当前位置值记为$1$
利用前缀和快速求出同种元素构成区间中的值的和, 并计入答案。
最后输出答案 $\div$ 2
利用树状数组可以查询前缀和, 单点修改。
代码
1 #include<cstring> 2 #include<cstdio> 3 #include<algorithm> 4 #define rd read() 5 using namespace std; 6 7 const int N = 1e6 + 1e5; 8 9 int l[N], sum[N], n; 10 11 int read() { 12 int X = 0, p = 1; char c = getchar(); 13 for(; c > '9' || c < '0'; c = getchar()) if(c == '-') p = -1; 14 for(; c >= '0' && c <= '9'; c = getchar()) X = X * 10 + c - '0'; 15 return X * p; 16 } 17 18 int lowbit(int x) { 19 return x & (-x); 20 } 21 22 void add(int x, int d) { 23 for(; x <= 2 * n; x += lowbit(x)) sum[x] += d; 24 } 25 26 int query(int x) { 27 int re = 0; 28 for(; x; x -= lowbit(x)) re += sum[x]; 29 return re; 30 } 31 32 int main() 33 { 34 n = rd; 35 int ans = 0; 36 for(int i = 1; i <= 2 * n; ++i) { 37 int x = rd; 38 if(l[x]) { 39 ans += query(i) - query(l[x]); 40 add(l[x], -2); 41 add(i, 1); 42 } 43 else { 44 add(i, 1); 45 l[x] = i; 46 } 47 } 48 printf("%d\n",ans >> 1); 49 }