DTOJ #3194. 去月球
【题目描述】
$Scape$ 和 $Mythological$ 交流了玩几何冲刺的经验之后,$Mythological$ 非常高兴,又推荐给 $Scape$ 一款游戏 $To the moon$。
游戏中一位老人 $John$ 的记忆被药物尘封,进行了解除尘封的仪式之后,$Scape$ 走入了他的记忆。
$John$ 的记忆中有一个唤做 $River$ 女孩的身影,有着数不尽的纸兔子,有一个沙包,一只鸭嘴兽玩偶,一座灯塔。$Scape$ 被深深的打动了。
”我的鸭嘴兽和沙包又在哪里呢?” $Scape$ 这样想到,不禁幻想出 $Mythological$ 决定给他送 $n$ 份礼物,其中第 $i$ 份的种类是 $a_i$。这些礼物按顺序排成一行。
她挑选礼物的方式很特别,她每次会选择两份种类相同的礼物,并且这对礼物满足它们之间没有尚未拿走的礼物,并将这对礼物拿走。
现在Scape给出了若干次询问,每次询问如果他送给她的是区间 $[L_i,R_i]$ 之间的礼物,那么她最多能拿走多少份礼物,询问强制在线。
【输入格式】
第 $1$ 行,三个整数 $n,m,q$ 分别是这组测试数据的大小,$a_i$ 的最大可能值,询问个数。
第 $2$ 行, $n$ 个整数,第 $i$ 个整数表示 $a_i$。
接下来 $q$ 行,每行两个整数 $L_i,R_i$,设 $las$ 为上一个询问的答案(初始为 $0$), 则询问的区间为 $[L_i ^ \land las,R_i^ \land las]$(保证合法)。
【输出格式】
共 $q$ 行,表示每个询问的答案。
【样例】
样例输入
样例输入1
9 10 3
1 1 3 2 1 1 2 3 1
2 9
1 2
2 5
样例输入2
10 10 3
1 2 3 4 5 6 7 8 9 10
1 9
2 3
4 5
样例输出
样例输出1
8
2
0
样例输出2
0
0
0
【数据范围与提示】
对于 $20\%$ 的数据,$1\leq n \leq 10^3,1\leq q\leq 10^3$。
对于 $80\%$ 的数据,$1\leq n \leq 10^5,1\leq q\leq 10^5$。
对于 $100\%$ 的数据,$1\leq n \leq 10^5,1\leq m \leq 10^5,1\leq q\leq 2\times 10^6$。
【题解】
首先有一个性质,从左往右能删掉的区间就直接删。不难证明这一定是最优的。
于是就可以对于每个询问从左往右扫一遍,用个栈维护即可。效率 $O(n^2)$。
考虑优化。我们发现,对于一段区间的起点,这个起点一定属于某一个可删除块,而这个起点向后的一部分和向前的一部分应该是相同的。这样向后的一段弹栈的过程就可以理解为在前面一段的压栈过程。
所以一个区间的可删除的点个数就相当于压栈的次数的两倍。压栈次数不好算,我们考虑不删除的部分。我们把栈用一棵 $Trie$ 树维护,用 $map$ 维护每个节点的儿子。
从根节点出发,如果新加入的节点与当前节点颜色相同,执行弹栈操作,跳回父亲节点。如果颜色不同,就沿着该颜色边往下跳。这样每一段可删除的都已经被压缩到一个点里了。
在这棵 $Trie$ 上,每次询问的 $l-1$ 和 $r$ 对应的节点间的距离就是无法删除的部分。往上跳的部分就是对称的部分,而往下跳的部分就刚好是后面多的部分。
由于只求两点间距离,用 $RMQ$ 维护即可。
【代码】
#include<bits/stdc++.h> namespace IO { char C[1 << 25], D[1 << 25], *S = C; int T = 0; inline void init (void) { fread (C, 1, 1 << 25, stdin); } inline int read (void) { int x = 0; char c; do c = *S++; while ( c < '0' or c > '9' ); do x = ( x << 1 ) + ( x << 3 ) + c - '0', c = *S++; while ( c >= '0' and c <= '9' ); return x; } inline void write (int x) { if ( !x ) { D[T++] = '0'; D[T++] = '\n'; return; } char st[30]; int tp = 0; while ( x ) st[++tp] = x % 10 + 48, x /= 10; while ( tp ) D[T++] = st[tp--]; D[T++] = '\n'; } inline void flush (void) { fwrite (D, 1, T, stdout); } } const int maxn=200000+10; int st[maxn][19],fst[maxn],fa[maxn],tot,dep[maxn],Log[maxn],a[maxn],n,m,q,A[maxn]; std::unordered_map<int,int> ch[maxn]; using namespace IO; std::vector<int> E[maxn]; inline void dfs ( int u ) { st[fst[u]=++tot][0]=dep[u]; for ( int v:E[u] ) dfs(v),st[++tot][0]=dep[u]; } inline int lca ( int u,int v ) { int ql=fst[u],qr=fst[v]; if ( ql>qr ) std::swap(ql,qr); int k=Log[qr-ql+1]; return std::min(st[ql][k],st[qr-(1<<k)+1][k]); } signed main() { init();n=read();m=read();q=read(); int u=0; for ( int i=1;i<=n;i++ ) { a[i]=read()+1; if ( a[i]==a[u] ) u=fa[u]; else { if ( !ch[u].count(a[i]) ) ch[u][a[i]]=i,E[u].push_back(i),fa[i]=u,dep[i]=dep[u]+1; u=ch[u][a[i]]; } A[i]=u; } dfs(0); for ( int i=2;i<=tot;i++ ) Log[i]=Log[i>>1]+1; for ( int j=1;j<=Log[tot];j++ ) for ( int i=1;i+(1<<j)-1<=tot;i++ ) st[i][j]=std::min(st[i][j-1],st[i+(1<<(j-1))][j-1]); for ( int ans=0,l,r;q--; ) l=read()^ans,r=read()^ans,write(ans=r-l+1-dep[A[l-1]]-dep[A[r]]+2*lca(A[l-1],A[r])); flush(); return 0; }