Monkey King
我觉得这个题目翻译的特别好,真的。
这个就是左偏树的模板题啦。每次找到这俩猴子所属的堆,然后如果不一样的话,就把两个堆里面最牛叉的两只猴子战斗力减成一半。我们用左偏树维护,取猴子的时候直接取顶端,然后我们把这个点的权值改成原来一半,重新合并就行啦。
这题在做的时候莫名MLE……后来发现是自己在删除操作的时候忘记清空这个点的左右儿子信息了。一般左偏树的题是不需要清空,但是这道题里面需要修改之后合并回来,如果不清空可能会出很严重的问题。
看一下代码。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<set> #include<queue> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') #define lowbit(x) x & (-x) using namespace std; typedef long long ll; const int M = 500005; const int INF = 1000000009; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } int n,m,root[M],lc[M],rc[M],val[M],dis[M],x,y; int merge(int x,int y) { if(!x || !y) return x | y; if(val[x] < val[y]) swap(x,y); rc[x] = merge(rc[x],y); root[rc[x]] = x; if(dis[lc[x]] < dis[rc[x]]) swap(lc[x],rc[x]); dis[x] = dis[rc[x]] + 1; return x; } int del(int x) { int l = lc[x],r = rc[x]; root[lc[x]] = lc[x],root[rc[x]] = rc[x]; lc[x] = rc[x] = dis[x] = 0; return merge(l,r); } int find(int x) { return (root[x] == x) ? x : root[x] = find(root[x]); } void clear() { memset(root,0,sizeof(root)); memset(val,0,sizeof(val)); memset(dis,0,sizeof(dis)); memset(lc,0,sizeof(lc)); memset(rc,0,sizeof(rc)); dis[0] = -1; } int main() { while(scanf("%d",&n) != EOF) { clear(); rep(i,1,n) val[i] = read(),root[i] = i; m = read(); rep(i,1,m) { x = read(),y = read(); int r1 = find(x),r2 = find(y); if(r1 == r2) printf("-1\n"); else { val[r1] >>= 1,val[r2] >>= 1; int f = del(r1),g = del(r2); int k1 = merge(f,r1),k2 = merge(g,r2); int k3 = merge(k1,k2); printf("%d\n",val[k3]); } } } return 0; }
当你意识到,每个上一秒都成为永恒。