【bzoj4736/uoj#274】[清华集训2016]温暖会指引我们前行 语文题+LCT
题目描述
题解
语文题+LCT
对于这种语文题建议还是自己读题好一些。。。
读懂题后发现:由于温度互不相同,最大生成树上的路径必须走(不走的话温度大的边少了,字典序一定会更小),并且不能多走边(因为多走的话串会变长,长度大反而亏),因此答案就是最大生成树上的路径。
因此使用LCT维护最大生成树即可。维护两点之间权值最小的边。加边时如果已经连通,则判断加的边与最小的边那个大,如果新加的边大则删掉原来最小的边,加入新边。
至于边长度的处理,可以新建一个虚点代表这条边,点权位相应边长度。加边时连接虚点和x、虚点和y即可。
时间复杂度 $O(LCT·n\log n)$
#include <cstdio> #include <algorithm> #define N 400010 using namespace std; typedef pair<int , int> pr; int f[N] , v[N] , w[N] , fa[N] , c[2][N] , si[N] , sum[N] , rev[N] , px[N] , py[N]; pr mn[N]; char str[10]; int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); } inline void pushup(int x) { si[x] = si[c[0][x]] + si[c[1][x]] + 1; mn[x] = min(pr(v[x] , x) , min(mn[c[0][x]] , mn[c[1][x]])); sum[x] = sum[c[0][x]] + sum[c[1][x]] + w[x]; } inline void pushdown(int x) { if(rev[x]) { swap(c[0][c[0][x]] , c[1][c[0][x]]); swap(c[0][c[1][x]] , c[1][c[1][x]]); rev[c[0][x]] ^= 1 , rev[c[1][x]] ^= 1; rev[x] = 0; } } inline bool isroot(int x) { return c[0][fa[x]] != x && c[1][fa[x]] != x; } void update(int x) { if(!isroot(x)) update(fa[x]); pushdown(x); } inline void rotate(int x) { int y = fa[x] , z = fa[y] , l = (c[1][y] == x) , r = l ^ 1; if(!isroot(y)) c[c[1][z] == y][z] = x; fa[x] = z , fa[y] = x , fa[c[r][x]] = y , c[l][y] = c[r][x] , c[r][x] = y; pushup(y) , pushup(x); } inline void splay(int x) { int y , z; update(x); while(!isroot(x)) { y = fa[x] , z = fa[y]; if(!isroot(y)) { if((c[0][y] == x) ^ (c[0][z] == y)) rotate(x); else rotate(y); } rotate(x); } } inline void access(int x) { int t = 0; while(x) splay(x) , c[1][x] = t , pushup(x) , t = x , x = fa[x]; } inline void makeroot(int x) { access(x) , splay(x) , swap(c[0][x] , c[1][x]) , rev[x] ^= 1; } inline void link(int x , int y) { makeroot(x) , fa[x] = y; } inline void split(int x , int y) { makeroot(x) , access(y) , splay(y); } inline void cut(int x , int y) { split(x , y) , fa[x] = c[0][y] = 0 , pushup(y); } int main() { int n , m , i , id , x , y , z , t , l; scanf("%d%d" , &n , &m); mn[0] = pr(1 << 30 , 0); for(i = 1 ; i <= n ; i ++ ) f[i] = i , mn[i] = pr(v[i] = 1 << 30 , i) , sum[i] = w[i] = 0 , si[i] = 1; while(m -- ) { scanf("%s" , str); if(str[0] == 'f') { scanf("%d%d%d%d%d" , &id , &x , &y , &t , &l) , x ++ , y ++ , id ++ ;; px[id] = x , py[id] = y , mn[id + n] = pr(v[id + n] = t , id + n) , sum[id + n] = w[id + n] = l , si[id + n] = 1; if(find(x) != find(y)) link(id + n , x) , link(id + n , y) , f[f[x]] = f[y]; else { split(x , y); if(mn[y].first < t) z = mn[y].second - n , cut(z + n , px[z]) , cut(z + n , py[z]) , link(id + n , x) , link(id + n , y); } } else if(str[0] == 'm') { scanf("%d%d" , &x , &y) , x ++ , y ++ ; if(find(x) != find(y)) puts("-1"); else split(x , y) , printf("%d\n" , sum[y]); } else scanf("%d%d" , &id , &l) , splay(++id + n) , w[id + n] = l , pushup(id + n); } return 0; }