[NOI2014] 魔法森林

题目

按照aaa精灵从小到大排序

按顺序插入每一条边

加入第iii条边后的最小代价为a[i]a[i]a[i]加上从111到nnn的所有路径中最大bbb最小的路径代价

维护边权 把边在LCTLCTLCT中理解为点?——看题解代码其实就是建虚点

出现环时 选择最大的bbb删去即可

/*
一些不成型的想法:贪心??怎么贪两个呢??——先让其中一维变成有序再着重贪另一维
私以为此题这样不可做 passkruskal重构树——最小生成树
最小生成树上的两点间距离最小LCT维护最小生成树
a精灵从小到大排序 插入b精灵??边权改成点权 构造虚点
*/
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 150005
#define inf 0x7f7f7f7f
struct node {int u, v, a, b;
}edge[maxn];
struct LCT {int f, val, rev, id;int son[2];
}t[maxn];
int n, m, ans = inf;
int st[maxn], w[maxn];bool cmp( node x, node y ) {return ( x.a == y.a ) ? ( x.b < y.b ) : ( x.a < y.a );
}bool isroot( int x ) {return t[t[x].f].son[0] == x || t[t[x].f].son[1] == x;
}void pushup( int x ) {t[x].id = x, t[x].val = w[x];if( t[x].son[0] && t[t[x].son[0]].val > t[x].val ) {t[x].val = t[t[x].son[0]].val;t[x].id = t[t[x].son[0]].id;}if( t[x].son[1] && t[t[x].son[1]].val > t[x].val ) {t[x].val = t[t[x].son[1]].val;t[x].id = t[t[x].son[1]].id;}
}void rotate( int x ) {int fa = t[x].f, Gfa = t[fa].f, k = t[fa].son[1] == x;if( isroot( fa ) ) t[Gfa].son[t[Gfa].son[1] == fa] = x;t[x].f = Gfa;if( t[x].son[k ^ 1] ) t[t[x].son[k ^ 1]].f = fa;t[fa].son[k] = t[x].son[k ^ 1];t[x].son[k ^ 1] = fa;t[fa].f = x;pushup( fa );pushup( x );
}void pushdown( int x ) {if( t[x].rev ) {swap( t[x].son[0], t[x].son[1] );if( t[x].son[0] ) t[t[x].son[0]].rev ^= 1;if( t[x].son[1] ) t[t[x].son[1]].rev ^= 1;t[x].rev = 0;}
}void splay( int x ) {int top = 0, y = x;st[++ top] = y;while( isroot( y ) ) st[++ top] = y = t[y].f;while( top ) pushdown( st[top --] );while( isroot( x ) ) {int fa = t[x].f, Gfa = t[fa].f;if( isroot( fa ) ) ( ( t[Gfa].son[0] == fa ) ^ ( t[fa].son[0] == x ) ) ? rotate( x ) : rotate( fa );rotate( x );}
}void access( int x ) {for( int son = 0;x;son = x, x = t[x].f ) {splay( x );t[x].son[1] = son;pushup( x );}
}int FindRoot( int x ) {access( x );splay( x );while( t[x].son[0] ) {pushdown( x );x = t[x].son[0];}return x;
}void MakeRoot( int x ) {access( x );splay( x );t[x].rev ^= 1;pushdown( x );
}void link( int x, int y ) {MakeRoot( x );if( FindRoot( y ) != x ) t[x].f = y;
}void split( int x, int y ) {MakeRoot( x );access( y );splay( y );
}
bool Union( int x, int y ) {MakeRoot( x );return FindRoot( y ) == x;
}int main() {scanf( "%d %d", &n, &m );for( int i = 1;i <= m;i ++ )scanf( "%d %d %d %d", &edge[i].u, &edge[i].v, &edge[i].a, &edge[i].b );sort( edge + 1, edge + m + 1, cmp );for( int i = 1;i <= m;i ++ ) {w[i + n] = edge[i].b;int u = edge[i].u, v = edge[i].v;if( u == v ) continue;if( Union( u, v ) ) {split( u, v );if( t[v].val <= edge[i].b ) continue;int x = t[v].id;splay( x );t[t[x].son[0]].f = t[t[x].son[1]].f = 0;link( u, i + n ), link( i + n, v );}elselink( u, i + n ), link( i + n, v );if( Union( 1, n ) ) split( 1, n ), ans = min( ans, edge[i].a + t[n].val );}if( ans == inf ) printf( "-1\n" );else printf( "%d\n", ans );return 0;
}

[ZJOI2018]历史

题目

一句话题意: 给出一棵树,给定每一个点的accessaccessaccess次数,计算轻重链切换次数的最大值,带修改

先不考虑带修改的问题

点uuu,只有在uuu子树里的点进行accessaccessaccess才会影响答案,并且点独立,可以分开算

假设uuu的子树发生了两次accessaccessaccess,当且仅当这两次操作的点分属于uuu两个不同儿子的子树中,答案才会+1+1+1

要使答案最大,就是尽量让所有相邻发生的崛起都来自不同子树

相当于有[0,m][0,m][0,m]种颜色,每种颜色有aia_iai​个,最大化相邻颜色不同的个数

设tot=∑i=0mai,maxx=maxi=0m{ai}tot=\sum_{i=0}^ma_i,maxx=max_{i=0}^m\{a_i\}tot=∑i=0m​ai​,maxx=maxi=0m​{ai​}

答案则为min{t−1,2×(tot−maxx)}min\{t-1,2\times (tot-maxx)\}min{t−1,2×(tot−maxx)}

考虑先放最多的maxxmaxxmaxx个然后插空,对于剩下的pos=tot−maxxpos=tot-maxxpos=tot−maxx个

如果pos≤maxx−1pos\le maxx-1pos≤maxx−1,显然直接插空,贡献2×p2\times p2×p个

如果pos>maxx−1pos>maxx-1pos>maxx−1,一定可以通过不断来回插空,达到上界tot−1tot-1tot−1

在O(n)O(n)O(n)的时间搜一遍整棵树即可

现在加上修改

根据2×maxx≥tot+12\times maxx\ge tot+12×maxx≥tot+1这个分界线处理

为什么这个是分界线??当满足这个式子的时候,答案一定取后者2×(tot−maxx)2\times (tot-maxx)2×(tot−maxx)

令optiopt_iopti​表示iii子树的操作次数和,如果满足2×opti≥optfai+12\times opt_i\ge opt_{fa_i}+12×opti​≥optfai​​+1,就把i→fai\rightarrow fai→fa连为实边,否则为虚边

为什么这么定义??因为在这样的定义下,如果iii子树内操作jjj加上权值www,其只会影响jjj到根路径上的点的答案和虚边关系

因为对于实边,存在2(opti+w)≥(optfai+w)+12(opt_i+w)\ge (opt_{fa_i}+w)+12(opti​+w)≥(optfai​​+w)+1,即实边还是实边

并且答案Δ=2[(optfai+w)−(opti+w)]−2[optfai−opti]=0\Delta =2[(opt_{fa_i}+w)-(opt_i+w)]-2[opt_{fa_i}-opt_i]=0Δ=2[(optfai​​+w)−(opti​+w)]−2[optfai​​−opti​]=0 是不变的

对于怎么找到路径上的虚边,就写LCTLCTLCT,因为这里的操作是类似于accessaccessaccess的,只不过要判断一下虚实边之间的变换

ansansans减去点更新前的答案再加上更新后的答案即可

#include <cstdio>
#include <vector>
using namespace std;
#define int long long
#define maxn 400005
struct node {int fa, val, sum, thin_edge_sum;int son[2];
}t[maxn];
vector < int > G[maxn];
int n, m, ans;bool isroot( int x ) {return t[t[x].fa].son[0] == x || t[t[x].fa].son[1] == x;
}void pushup( int x ) {t[x].sum = t[x].val + t[t[x].son[0]].sum + t[t[x].son[1]].sum + t[x].thin_edge_sum;
} void rotate( int x ) {int fa = t[x].fa, Gfa = t[fa].fa, k = t[fa].son[1] == x;if( isroot( fa ) ) t[Gfa].son[t[Gfa].son[1] == fa] = x;t[x].fa = Gfa;if( t[x].son[k ^ 1] ) t[t[x].son[k ^ 1]].fa = fa;t[fa].son[k] = t[x].son[k ^ 1];t[x].son[k ^ 1] = fa;t[fa].fa = x;pushup( fa );pushup( x );
}void splay( int x ) {while( isroot( x ) ) {int fa = t[x].fa, Gfa = t[fa].fa;if( isroot( fa ) )( ( t[Gfa].son[1] == fa ) ^ ( t[fa].son[1] == x ) ) ? rotate( fa ): rotate( x );rotate( x );}
}int calc( int x, int tot, int maxx ) {if( t[x].son[1] ) return ( tot - maxx ) * 2;else if( t[x].val * 2 > tot ) return ( tot - t[x].val ) * 2;else return tot - 1;
}void modify( int x, int w ) {splay( x );/*如果想要得到某一个点的tot就应该计算splay里面深度≥它的点的val之和也就是将这个点splay到根后这个点的val加上其右子树的val和*/int tot = t[x].sum - t[t[x].son[0]].sum;int maxx = t[t[x].son[1]].sum;ans -= calc( x, tot, maxx );t[x].val += w, tot += w, t[x].sum += w;if( maxx * 2 < tot + 1 ) { //实边变虚边t[x].thin_edge_sum += maxx;t[x].son[1] = 0;}ans += calc( x, tot, maxx );pushup( x );int son;for( x = t[son = x].fa;x;x = t[son = x].fa ) {splay( x );int tot = t[x].sum - t[t[x].son[0]].sum;int maxx = t[t[x].son[1]].sum;ans -= calc( x, tot, maxx );t[x].thin_edge_sum += w, tot += w, t[x].sum += w;if( maxx * 2 < tot + 1 ) {t[x].thin_edge_sum += maxx;t[x].son[1] = 0;maxx = 0;}if( t[son].sum * 2 > tot ) {t[x].thin_edge_sum -= t[son].sum;t[x].son[1] = son;maxx = t[son].sum;}ans += calc( x, tot, maxx );pushup( x );}
}void dfs( int u ) {t[u].sum = t[u].val;int pos = 0, maxx = t[u].val;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i];if( v == t[u].fa ) continue;t[v].fa = u;dfs( v );t[u].sum += t[v].sum;if( t[v].sum > maxx ) maxx = t[v].sum, pos = v;}ans += min( t[u].sum - 1, ( t[u].sum - maxx ) * 2 );if( maxx * 2 >= t[u].sum + 1 ) t[u].son[1] = pos; //实边 修改是不会更改贡献的t[u].thin_edge_sum = t[u].sum - t[u].val - t[t[u].son[1]].sum;//所有虚边的操作数和 以后是会更改贡献的
}signed main() {scanf( "%lld %lld", &n, &m );for( int i = 1;i <= n;i ++ )scanf( "%lld", &t[i].val );for( int i = 1, u, v;i < n;i ++ ) {scanf( "%lld %lld", &u, &v );G[u].push_back( v );G[v].push_back( u );  }dfs( 1 );printf( "%lld\n", ans );for( int i = 1, x, y;i <= m;i ++ ) {scanf( "%lld %lld", &x, &y );modify( x, y );printf( "%lld\n", ans );}return 0;
}

[LCT动态树] [NOI2014]魔法森林,[ZJOI2018]历史相关推荐

  1. loj2245 [NOI2014]魔法森林 LCT

    [NOI2014]魔法森林 链接 loj 思路 a排序,b做动态最小生成树. 把边拆成点就可以了. uoj98.也许lct复杂度写假了..越卡常,越慢 代码 #include <bits/std ...

  2. 【bzoj3669】[Noi2014]魔法森林【LCT】

    [Noi2014]魔法森林 Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1 ...

  3. BZOJ 3669: [Noi2014]魔法森林( LCT )

    排序搞掉一维, 然后就用LCT维护加边MST. O(NlogN) ------------------------------------------------------------------- ...

  4. 神spfa [Noi2014]魔法森林

    问题 G: [Noi2014]魔法森林 时间限制: 30 Sec  内存限制: 512 MB 题目描述 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个 ...

  5. [BZOJ]3669: [Noi2014]魔法森林 lct

    Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...

  6. 图论 BZOJ 3669 [Noi2014]魔法森林

    Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...

  7. [NOI2014]魔法森林

    ★★★☆   输入文件:magicalforest.in   输出文件:magicalforest.out   简单对比 时间限制:2 s   内存限制:512 MB 题解: 每条边有两种权值,所以想 ...

  8. 【bzoj 3669】[Noi2014]魔法森林

    Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...

  9. [Luogu P2387] [NOI2014]魔法森林 (LCT维护边权)

    题面 传送门:https://www.luogu.org/problemnew/show/P2387 Solution 这题的思想挺好的. 对于这种最大值最小类的问题,很自然的可以想到二分答案.很不幸 ...

最新文章

  1. Ruby之父:写Ruby时工作特别闲,总加班的人很难做出创造
  2. 来自Facebook AI的多任务多模态的统一Transformer:向更通用的智能迈出了一步
  3. npm更改为淘宝镜像
  4. python3.8.2安装教程-Python3.8.2 软件介绍(附安装包)
  5. 【收藏】nvm的下载,安装与使用(nodejs版本管理)
  6. oracle查询执行过的sql语句,如何查询已经执行过的SQL语句曾经的执行花费时间
  7. 计组之存储系统:4、双口RAM和多模块存储器(存取周期、双端口RAM、多体并行存储器、存储体)
  8. 8086-汇编-模块化程序设计
  9. Kippo:一款强大的SSH蜜罐工具
  10. 重庆中职高考计算机专业试题,职业高中高考计算机专业试卷3答案
  11. DataGridView 列自适应宽度 设置
  12. 虚继承中的构造函数的调用
  13. Java程序开发过程
  14. hget hmget redis api使用
  15. 2019哪里可以进行高层次人才扶持政策申报?
  16. 微前端在Vue项目的实践
  17. 我的第一个博客----浅谈人生观价值观
  18. 中职学生学业水平计算机考试试题,中职学业水平测试试卷(综合卷).docx
  19. OneNote桌面版与UWP版避免自动切换字体的方案
  20. 2019年,SEO关键词KPI考核指标有哪些?

热门文章

  1. 没有标题,配得上这款“俄罗斯方块”
  2. 浮点数赋值给整数_初学者专题:变量和赋值
  3. 化妆definer是什么意思_化妆品上的r是什么意思
  4. python二维列表写入excel_用Python实现合并excel列表
  5. 模型存储在哪里_最强的模型工作收纳站「Artty Station」登场!
  6. as本地仓库更改_将gitee仓库连接GitHub Desktop。新建更改仓库并上传至gitee。
  7. c语言中文件如何插入数据,急求如何将下列C语言程序数据存储到文件中?
  8. 对象数组参数_【JavaScript 教程】标准库—Array 对象
  9. leetcode96. 不同的二叉搜索树
  10. 高等数学下-赵立军-北京大学出版社-题解-练习11.2