LCT维护MST+子树信息

看了好长时间题解

editorial

结论:像做最小生成树一样,当每个连通块都是偶数个点就停下来。

每次复杂度mlogm

口胡

首先我们发现奇数个点是不满足每个点度数为奇数,因为一条边贡献两个度数,所以度数一定是偶数,但是奇数个点每个点奇数度度数总和是奇数,所以点数一定是偶数。

但是这样不足以解决问题。于是我们转化一下,如果一个连通块有偶数个点,那么这个连通块一定能满足条件。这里要注意,是满足条件,但是我们维护的东西并不满足条件。我们只维护最小生成森林,这样是不保证度数是奇数的,但是如果在森林上删去一些边,肯定能满足。既然多出来了一些边,怎么查找答案呢?我们每次加边,如果两个顶点不连通,那么我们连接,同时统计奇数个顶点的连通块,如果联通,那么我们删去形成的环上最大的边。然后把插入的边放进set里。每次查找,我们按边权从大到小检查,看这条边能否删去,能删去的条件是删掉这条边后两个连通块的点数都为偶数。删到不能删停下来。如果删掉一条边分裂成两个奇数大小的连通块,那么肯定是不满足条件的。这里就是解法的巧妙所在,我们保留一些可能没用的边,也不在乎性质,只有删边的时候才考虑。

然后还要维护子树信息。lct一般只能维护链上信息,但是我们发现,只有access和link和cut会改变子树之间的关系,那么我们就在这两个时候维护点权。(这里不是很清楚)

每次查询size的时候就把这个点转到根,查询即可。

好久没写lct都快忘了

#include<bits/stdc++.h>
using namespace std;
const int N = ;
struct edge {
int u, v, w, id;
edge(int u = , int v = , int w = , int id = ) : u(u), v(v), w(w), id(id) { }
bool operator < (edge e) const
{
return w == e.w ? id < e.id : w > e.w;
}
};
vector<edge> ed;
set<edge> s;
int n, m, o;
namespace lct
{
int top;
int fa[N], child[N][], size[N], v[N], tag[N], st[N], mx[N], id[N];
bool isroot(int x) { return !fa[x] || (child[fa[x]][] != x && child[fa[x]][] != x); }
void update(int x)
{
size[x] = size[child[x][]] + size[child[x][]] + v[x];
mx[x] = id[x];
if(ed[mx[child[x][]]].w > ed[mx[x]].w) mx[x] = mx[child[x][]];
if(ed[mx[child[x][]]].w > ed[mx[x]].w) mx[x] = mx[child[x][]];
}
void pushdown(int x)
{
if(!tag[x]) return;
tag[child[x][]] ^= ;
tag[child[x][]] ^= ;
swap(child[x][], child[x][]);
tag[x] ^= ;
}
void zig(int x)
{
int y = fa[x];
fa[x] = fa[y];
if(!isroot(y)) child[fa[x]][child[fa[x]][] == y] = x;
child[y][] = child[x][];
fa[child[x][]] = y;
fa[y] = x;
child[x][] = y;
update(y);
update(x);
}
void zag(int x)
{
int y = fa[x];
fa[x] = fa[y];
if(!isroot(y)) child[fa[x]][child[fa[x]][] == y] = x;
child[y][] = child[x][];
fa[child[x][]] = y;
fa[y] = x;
child[x][] = y;
update(y);
update(x);
}
void splay(int x)
{
top = ;
st[++top] = x;
for(int now = x; !isroot(now); now = fa[now]) st[++top] = fa[now];
for(int i = top; i; --i) pushdown(st[i]);
while(!isroot(x))
{
int y = fa[x], z = fa[y];
if(isroot(y))
{
child[y][] == x ? zig(x) : zag(x);
break;
}
else if(child[y][] == x && child[z][] == y) { zig(y); zig(x); }
else if(child[y][] == x && child[z][] == y) { zag(y); zag(x); }
else if(child[y][] == x && child[z][] == y) { zag(x); zig(x); }
else if(child[y][] == x && child[z][] == y) { zig(x); zag(x); }
}
}
void access(int x)
{
for(int y = ; x; y = x, x = fa[x])
{
splay(x);
v[x] -= size[y];
v[x] += size[child[x][]];
child[x][] = y;
update(x);
}
}
void rever(int x)
{
access(x);
splay(x);
tag[x] ^= ; //x本来是最深的,反转深度
}
void link(int x, int y)
{
rever(x);
fa[x] = y;
v[y] += size[x];
}
void cut(int x, int y)
{
rever(x); //u是最浅的
access(y);
splay(y);
fa[x] = child[y][] = ;
v[y] -= size[x];
update(y);
}
int find(int x)
{
access(x);
splay(x);
while(child[x][]) x = child[x][];
return x;
}
int q(int x)
{
rever(x);
return size[x];
}
} using namespace lct;
void add_edge(edge e)
{
if(q(e.u) & && q(e.v) & ) o -= ;
id[e.id + n] = e.id;
mx[e.id + n] = e.id;
link(e.u, e.id + n);
link(e.v, e.id + n);
}
int delete_edge(edge e)
{
cut(e.u, e.id + n);
cut(e.v, e.id + n);
if(q(e.u) & && q(e.v) & ) o += ;
return !(q(e.u) & );
}
int max_cost()
{
if(o) return -;
while(delete_edge(*s.begin())) s.erase(*s.begin());
add_edge(*s.begin());
return (*s.begin()).w;
}
int find_max(int u, int v)
{
rever(u);
access(v);
splay(v);
return mx[v];
}
void new_edge(edge e)
{
e.id = ed.size();
ed.push_back(e);
if(find(e.u) == find(e.v))
{
int id = find_max(e.u, e.v);
if(ed[id].w <= e.w) return;
delete_edge(ed[id]);
}
add_edge(e);
s.insert(e);
}
int main()
{
scanf("%d%d", &n, &m);
o = n;
ed.push_back(edge(, , , ));
for(int i = ; i <= n; ++i) v[i] = ;
for(int i = ; i <= m; ++i)
{
edge e;
scanf("%d%d%d", &e.u, &e.v, &e.w);
new_edge(e);
printf("%d\n", max_cost());
}
return ;
}

603E的更多相关文章

  1. Codeforces 603E Pastoral Oddities

    传送门:http://codeforces.com/problemset/problem/603/E [题目大意] 给出$n$个点,$m$个操作,每个操作加入一条$(u, v)$长度为$l$的边. 对 ...

  2. [转帖]IBM POWER系列处理器的前世今生

    IBM POWER系列处理器的前世今生 Power是Power Optimization With Enhanced RISC的缩写,是由IBM开发的一种RISC指令集架构(ISA). IBM的很多服 ...

  3. 转帖 IBM要推POWER9,来了解一下POWER处理器的前世今生

    https://blog.csdn.net/kwame211/article/details/76669555 先来说一下最新的POWER 9 在Hot Chips会议上首次提到的IBM Power ...

  4. VINS(二)Feature Detection and Tracking

    系统入口是feature_tracker_node.cpp文件中的main函数 1. 首先创建feature_tracker节点,从配置文件中读取信息(parameters.cpp),包括: ROS中 ...

  5. NOIP2017提高组 模拟赛13(总结)

    NOIP2017提高组 模拟赛13(总结) 第一题 函数 [题目描述] [输入格式] 三个整数. 1≤t<10^9+7,2≤l≤r≤5*10^6 [输出格式] 一个整数. [输出样例] 2 2 ...

随机推荐

  1. DECLARE_MESSAGE_MAP( )

    DECLARE_MESSAGE_MAP( ) 说明: 你的程序中的每一个CCmdTarget的派生类都可以提供一个消息映射以处理消息.在你的类声明的末尾使用DECLARE_MESSAGE_MAP宏.然 ...

  2. spring IOC bean中注入集合

    建立一个实体 package com.java.test4; import java.util.*; /** * @author nidegui * @create 2019-06-22 14:45 ...

  3. 图表实现基于SVG或Canvas

    Highcharts 基于SVG,方便自己定制,但图表类型有限. Echarts 基于Canvas,适用于数据量比较大的情况. D3.v3 基于SVG,方便自己定制:D3.v4支持Canvas+SVG ...

  4. 奇怪的print progname ":\n"日志

    [root@xxxxxxxx /home/ahao.mah] #tail /var/log/messages -f Feb 10 10:01:01 csaccurate-49-5011 } Feb 1 ...

  5. inet_XX族函数

    在网络编程中, 经常会将网络字节转为本地字节或者将本地字节转为网络字节, 但是如果每次我们都是都通过htonl, ntohl函数需要将10进制转为整数, 甚至还用将字符串转为整数, 再转为网络字节, ...

  6. NOIP2018 滚粗记

    Day -2  上午,大家都在复习各种模板,zhx总结了足足67个模板(杨辉三角也算模板???),lgl死磕FFT发现cos和sin打反了,我也是复习板子和以前做过的题,几乎没有人颓. 接着jdr,l ...

  7. TortoiseGit配置密钥的方法

    TortoiseGit 使用扩展名为ppk的密钥,而不是ssh-keygen生成的rsa密钥.使用命令ssh-keygen -C "邮箱地址" -t rsa产生的密钥在Tortoi ...

  8. 【codeforces 508B】Anton and currency you all know

    [题目链接]:http://codeforces.com/contest/508/problem/B [题意] 给你一个奇数; 让你交换一次数字; 使得这个数字变成偶数; 要求偶数要最大; [题解] ...

  9. kafka监控工具kafka-manager

    1.几个kafka监控工具 Kafka Web Console:监控功能较为全面,可以预览消息,监控Offset.Lag等信息,但存在bug,不建议在生产环境中使用. Kafka Manager:偏向 ...

  10. MySql 内存表使用

    MySql 内存表使用 内存表使用哈希散列索引把数据保存在内存中,因此具有极快的速度,适合缓存中小型数据库,但是使用上受到一些限制,以下是蓝草使用的一些感受. 1.heap对所有用户的连接是可见的,这 ...