您的位置:首页 > 其它

【bzoj4736/uoj#274】[清华集训2016]温暖会指引我们前行 语文题+LCT

2017-12-19 11:12 302 查看
题目描述

http://uoj.ac/problem/274

题解

语文题+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
, v
, w
, fa
, c[2]
, si
, sum
, rev
, px
, py
;
pr mn
;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: