2
替换颜色的时候可以采用启发式合并,把小的往大的里面塞,然后记住每个颜色在当前序列中的颜色,然后维护一个树状数组,统计相邻位置的情况即可。
此题与梦幻布丁一样,不懂的话可以先去看 bzoj1483这道题。 这里讲的很好,可以点进去看看-->bzoj1483 (hnoi2009)梦幻布丁(链表+启发式合并)
代码:参考_Occult_
#include<cmath> #include<queue> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define ms(x,y) memset(x,y,sizeof(x)) #define rep(i,j,k) for(int i=j;i<=k;i++) #define loop(i,j,k) for (int i=j;i!=-1;i=k[i]) #define inone(x) scanf("%d",&x) #define intwo(x,y) scanf("%d%d",&x,&y) #define inthr(x,y,z) scanf("%d%d%d",&x,&y,&z) #define lson x<<1,l,mid #define rson x<<1|1,mid+1,r typedef long long LL; const int low(int x) { return x&-x; } const int mod = 1e9 + 7; const int N = 1e6 + 10; int T, n, m, x, y, z; int ft[N], nt[N], u[N], sz; int f[N], g[N], a[N], q[N]; void add(int x, int y) { for (int i = x; i <= n; i += low(i)) q[i] += y; } int sum(int x) { int s = 0; for (int i = x; i; i -= low(i)) s += q[i]; return s; } int main() { inone(T); while (T--) { intwo(n, m); ms(ft, -1); ms(f, 0); ms(g, 0); ms(q, 0); sz = 0; a[0] = a[n + 1] = -1; rep(i, 1, n) { inone(a[i]); f[a[i]] = a[i]; g[a[i]]++; u[sz] = i; nt[sz] = ft[a[i]]; ft[a[i]] = sz++; if (a[i] != a[i - 1]) add(i, 1); } while (m--) { inthr(x, y, z); if (x == 2) printf("%d\n", sum(z) - sum(y - 1) + (int)(a[y] == a[y - 1])); else { if (y == z || !g[f[y]]) continue; int s = y, t = z; if (g[f[y]] > g[f[z]]) swap(y, z); y = f[y]; z = f[z]; loop(i, ft[y], nt) { if (a[u[i]] != a[u[i] - 1]) add(u[i], -1); if (a[u[i]] != a[u[i] + 1]) add(u[i] + 1, -1); a[u[i]] = z; if (a[u[i]] != a[u[i] - 1]) add(u[i], 1); if (a[u[i]] != a[u[i] + 1]) add(u[i] + 1, 1); if (nt[i] == -1) { nt[i] = ft[z]; ft[z] = ft[y]; break; } } g[z] += g[y]; g[y] = 0; f[t] = z; f[s] = 0; } } } return 0; }