花花的森林,嗯,这是一篇正经的题解。

模拟考的时候没有看出来要怎么求啊,暴力地树形DP、换根、合并、求直径。居然也险险地拿到了80分,不过我们要正经地想正解。

容易想到我们可以让时光倒流,让空间扭转,离线地从最后一次操作往前,这样删边就变成加边了(合并总比拆开好做吧)

首先,我们要知道,两棵树合并后,新直径的两个端点一定是原来两棵树的两个直径的四个不同端点中的某两个(为什么?),自己画一下图,思考证明一下,这个结论并不难得出。

所以说合并时的新直径就只会有六种即 C(4,2),那么我们分别求出这六个直径的值(当然原本的两种不用求),用最大的作为新树的直径就好啦~,并且记下此时的两个端点。

那要怎么求直径啊~?

当然是倍增啦。

可是可是,我们都把树拆成一棵棵了,怎么倍增啊,合并后不是还要重新算一次倍增的数组吗?

我们可以在原树上倍增呀,没必要真的把树拆了(如果题目让你做什么你就做什么,一定会被带离正解的),在一棵树上,两点之间的简单路径是唯一的。

(那你真是个小机灵鬼~)

然后合并就用并查集维护。

哦对了,还有一个细节,从后往前做我们会遇到除法的问题(难不成你要循环一遍整个森林求一次ans吗)所以在模质数的意义下,使用费马小定理求逆元来进行除法。

 #include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm> #define For(i,a,b) for(register int i=a;i<=b;++i)
#define Dwn(i,a,b) for(register int i=a;i>=b;--i)
#define Re register
#define Pn putchar('\n')
#define llg long long
using namespace std;
const int N=1e5+,md=1e9+;
llg a[N],ans=,dt[N],pt[N][],fn[N];
int head[N],nxt[N*],v[N*],cnt=;
int pa[N],sz[N],n,m,x,y,Q[N];
int f[N][];
llg d[N][];
struct EDG{
int a,b;
}ed[N];
inline void read(int &v){
v=;
char c=getchar();
while(c<''||c>'')c=getchar();
while(c>=''&&c<='')v=v*+c-'',c=getchar();
}
inline void read(llg &v){
v=;
char c=getchar();
while(c<''||c>'')c=getchar();
while(c>=''&&c<='')v=v*+c-'',c=getchar();
}
void write(llg x){
if(x>)write(x/);
int xx=x%;
putchar(xx+'');
}
int find(int x){
int r=x;
while(pa[r]!=r)r=pa[r];
int i,j;
i=x;
while(pa[i]!=r){
j=pa[i]; pa[i]=r; i=j;
}
return r;
}
void add(int ux,int vx){
cnt++;
nxt[cnt]=head[ux]; head[ux]=cnt; v[cnt]=vx;
cnt++;
nxt[cnt]=head[vx]; head[vx]=cnt; v[cnt]=ux;
}
llg Qsm(llg x,int k){
llg ans=;
while(k){
if(k&)ans=(ans*x)%md;
x=(x*x)%md;
k>>=;
}
return ans;
} int dep[N];
void DFS(int x,int fa){ for(Re int i=head[x];i;i=nxt[i]){
int vv=v[i]; if(vv==fa)continue; f[vv][]=x; d[vv][]=a[vv];
dep[vv]=dep[x]+; DFS(vv,x);
}
} llg getDis(int x,int y){
llg as=;
if(dep[x]<dep[y])swap(x,y); Dwn(b,,)if(f[x][b]!=-){
if(dep[f[x][b]]>=dep[y]){
as+=d[x][b]; x=f[x][b];
}
} if(x==y)return as+a[x];
Dwn(b,,)if(f[x][b]!=-&&f[y][b]!=-&&f[x][b]!=f[y][b]){
as+=d[x][b]; as+=d[y][b];
x=f[x][b]; y=f[y][b];
}
as+=d[x][]; as+=d[y][]; return as+a[f[x][]];
} llg d00,d10,d01,d11,kd,dx=,px1,px2; int main(){
freopen("forest.in","r",stdin);
freopen("forest.out","w",stdout);
read(n);
For(i,,n){
read(a[i]);
ans=(ans*a[i])%md;
pt[i][]=pt[i][]=i; dt[i]=a[i];
}
For(i,,n-){
read(ed[i].a); read(ed[i].b);
add(ed[i].a,ed[i].b);
}
memset(f,-,sizeof(f)); DFS(,); For(b,,) For(i,,n){
if(f[i][b-]==-)continue; f[i][b]= f[ f[i][b-] ][b-];
d[i][b]= d[ f[i][b-] ][b-]+d[i][b-];
} For(i,,n)pa[i]=i; For(i,,n-)read(Q[i]);
fn[n]=ans;
Dwn(i,n-,){
int qs=Q[i];
x=ed[qs].a; y=ed[qs].b;
int pax=find(x),pay=find(y); ans=(ans*Qsm(dt[pax],md-))%md;
ans=(ans*Qsm(dt[pay],md-))%md; if(dt[pax]>dt[pay]){
dx=dt[pax]; px1=pt[pax][]; px2=pt[pax][];
}else{
dx=dt[pay]; px1=pt[pay][]; px2=pt[pay][];
} d00=getDis(pt[pax][],pt[pay][]);
if(d00>dx)dx=d00,px1=pt[pax][],px2=pt[pay][]; d10=getDis(pt[pax][],pt[pay][]);
if(d10>dx)dx=d10,px1=pt[pax][],px2=pt[pay][]; d01=getDis(pt[pax][],pt[pay][]);
if(d01>dx)dx=d01,px1=pt[pax][],px2=pt[pay][]; d11=getDis(pt[pax][],pt[pay][]);
if(d11>dx)dx=d11,px1=pt[pax][],px2=pt[pay][]; pt[pax][]=px1; pt[pax][]=px2; dt[pax]=dx;
ans=(ans*dx)%md;
pa[pay]=pax;
fn[i]=ans;
}
For(i,,n){
write(fn[i]); Pn;
}
return ;
}

花花的森林(倍增,LCA的更多相关文章

  1. 【Luogu】P1967货车运输(最大生成森林+倍增LCA)

    题目链接 倍增LCA是个什么蛇皮原理啊,循环完了还得再往上跳一次才能到最近公共祖先 合着我昨天WA两次就是因为这个 建最大生成森林,因为图不一定是联通的,所以不一定是一棵树.这个地方用克鲁斯卡尔就好了 ...

  2. 【bzoj3123】[Sdoi2013]森林 倍增LCA+主席树+启发式合并

    题目描述 输入 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负 ...

  3. JZOJ 5286. 【NOIP2017提高A组模拟8.16】花花的森林 (Standard IO)

    5286. [NOIP2017提高A组模拟8.16]花花的森林 (Standard IO) Time Limits: 1000 ms Memory Limits: 131072 KB Descript ...

  4. 货车运输(最大生成树+倍增LCA)

    看到第一篇题解的神奇码风--我决定发一篇码风正常的题解造福人类 这题的做法也非常经典,最大生成树\(+LCA\),相当于先贪心一下,在LCA的时候记录一下当前最小的边权 顺便吐槽一下最后一个测试点: ...

  5. 【bzoj2815】[ZJOI2012]灾难 拓扑排序+倍增LCA

    题目描述(转自洛谷) 阿米巴是小强的好朋友. 阿米巴和小强在草原上捉蚂蚱.小强突然想,果蚂蚱被他们捉灭绝了,那么吃蚂蚱的小鸟就会饿死,而捕食小鸟的猛禽也会跟着灭绝,从而引发一系列的生态灾难. 学过生物 ...

  6. 【bzoj4242】水壶 BFS+最小生成树+倍增LCA

    题目描述 JOI君所居住的IOI市以一年四季都十分炎热著称. IOI市是一个被分成纵H*横W块区域的长方形,每个区域都是建筑物.原野.墙壁之一.建筑物的区域有P个,编号为1...P. JOI君只能进入 ...

  7. [板子]倍增LCA

    倍增LCA板子,没有压行,可读性应该还可以.转载请随意. #include <cstdio> #include <cstring> #include <algorithm ...

  8. 洛谷P3128 [USACO15DEC]最大流Max Flow [倍增LCA]

    题目描述 Farmer John has installed a new system of  pipes to transport milk between the  stalls in his b ...

  9. Gym100685G Gadget Hackwrench(倍增LCA)

    题目大概说一棵边有方向的树,q个询问,每次询问结点u是否能走到v. 倍增LCA搞即可: 除了par[k][u]表示u结点往上走2k步到达的结点, 再加上upp[k][u]表示u结点往上走2k步经过边的 ...

随机推荐

  1. Shell中括号的作用

    Shell中括号的作用 作者:Danbo 时间:2015-8-7 单小括号() ①.命令组.括号中的命令将会断开一个子Shell顺序执行,所以括号中的变量不能被脚本余下的部分使用.括号中多个命令之间用 ...

  2. 在springboot中使用Mybatis Generator的两种方式

    介绍 Mybatis Generator(MBG)是Mybatis的一个代码生成工具.MBG解决了对数据库操作有最大影响的一些CRUD操作,很大程度上提升开发效率.如果需要联合查询仍然需要手写sql. ...

  3. Android中点击事件的处理解析及常见问题

          当我们手指按下时,Android采用层层传递-冒泡的方式处理点击事件.例如,现在公司来了个小项目,老板一看分配给经理做,经理一看分配给小组长,小组长一看好简单,分配给组员.如果在这个传递过 ...

  4. 数据结构之 图论---连通分量的个数(dfs搜索)

    数据结构实验:连通分量个数 Time Limit: 1000MS Memory limit: 65536K 题目描述  在无向图中,如果从顶点vi到顶点vj有路径,则称vi和vj连通.如果图中任意两个 ...

  5. hdu1198 Farm Irrigation —— dfs or 并查集

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1198 dfs: #include<cstdio>//hdu1198 dfs #includ ...

  6. Java 8新特性之旅:使用Stream API处理集合

    在这篇“Java 8新特性教程”系列文章中,我们会深入解释,并通过代码来展示,如何通过流来遍历集合,如何从集合和数组来创建流,以及怎么聚合流的值. 在之前的文章“遍历.过滤.处理集合及使用Lambda ...

  7. Jmeter创建一个简单的http接口用例

    1.新建线程组 添加->Threads(Users)->线程组 线程组用来模拟用户进程. 2.添加http信息头管理器 添加->配置元件->http信息头管理器 Systemi ...

  8. #pragma once与#ifndef

    都是为了避免同一个头文件被包含多次.在编译器对这两种方式都支持的情况下,区别很小. 方式一: #pragma once ...//这里放声明语句 方式二: #ifndef 宏名 #def 宏名 ... ...

  9. BZOJ 1632 [Usaco2007 Feb]Lilypad Pond:spfa【同时更新:经过边的数量最小】【路径数量】

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1632 题意: 有一个n*m的池塘.0代表水,1代表荷花,2代表岩石,3代表起点,4代表终点 ...

  10. Loadrunner进行性能测试的步骤

    Loadrunner 11是一款免费的性能测试工具,他包含三个大模块 •使用VuGen:创建脚本•运用Controller:设置方案•查看Analysis:分析测试结果 结合软件测试的流程可以知道使用 ...