计蒜客 444 / xtuoj 1024 京东的物流路径(并查集+离线lca)或者 (点分治)
题意:
一颗树,定义一条路径的权值等于路径的边权之和,需要求这颗树所有路径中权值的最大值
思路:
考虑到路径权值与点权的最值有关,而最值的问题通常可以通过排序就行处理,于是想到先把点权排序。
容易看出如果某条路径的权值是通过某个点算出的最小 ,那么肯定这条路径肯定不会经过权值更小的点,于是有了两种处理思路
1.按点权从小到大删点,对于即将删除的点,比他权值小的点已经被删去了,所以只要在当前状态的森林里找一条最长路径乘以次点权就可以更新答案
2.按点权从大到小加点,显然新加进来的点权值最小,当前树里的任何路径的点权最小值都不会小于新加进来的点,所以可以通过维护直径来更新答案
对于思路1,如果是一条链的话,对于一个将要删除的点,可以直接二分得到当前点附近已经被删除的点并更新答案,然而在树里面就不太好从处理了,至少我肯定是不会的
于是考虑思路2:
加点建树很明显可以通过并查集维护
但是在树的合并过程中怎么维护直径呢?这里需要用到一个定理。。一颗树的直径的两个端点一定是他子树直径的端点
于是就可以进行直径的维护了,具体求距离可以选择logn的lca
代码:
#include <bits/stdc++.h>
using namespace std;
long long f[];
// 节点编号[1,n]
// 最大节点数
#define MAXN 200010
// 满足2^M > MAXN
#define M 20
struct node
{
long long to,val;
};
struct tree
{
long long r,x,y;
void clear()
{
r=;
x=y=-;
}
}t[];
vector<node>g[];
long long d[];
long long n;
int a[];
long long ans;
long long ch[MAXN]={}; // 节点i下面的孩子的个数(包括i)
long long fa[MAXN][M+]; // ch[i][j]表示与节点i距离为2^j的i的祖先
long long len[MAXN][M+];
long long deep[MAXN]={}; // i的深度(根节点的深度为1)
// 初始化deep数组,ch数组,fa数组的初始值
// 默认根节点为1
long long dfs(int cur=, int father = ,long long val=)
{
deep[cur] = deep[father]+;
fa[cur][] = father;
len[cur][]=val;
ch[cur] = ;
int sz = g[cur].size();
for(int i=;i<sz;i++)
{
if(g[cur][i].to != father)
ch[cur] += dfs(g[cur][i].to,cur,g[cur][i].val);
}
return ch[cur];
}
// 初始化fa数组
void initFa(int n)
{
for(int i=;i<=;i++)
for(int node=;node<=n;node++)
{
fa[node][i] = fa[fa[node][i-]][i-];
len[node][i]= len[fa[node][i-]][i-]+len[node][i-];
}
}
// 将node上升height的高度
int binaryRaise(int node, int height,long long &val)
{
val=;
for(int i=M; i>=; i--)
{
if(fa[node][i] && height >= (<<i))
{
val+=len[node][i];
node = fa[node][i];
height -= (<<i);
}
}
return node;
}
// a的深度比b大
long long lca(int a, int b)
{
if(a==||b==)
return -;
// 先移动a到与b同样深度的地方
if(deep[a]<deep[b])
swap(a,b);
long long res=;
a = binaryRaise(a, deep[a]-deep[b],res);
if(a==b) // 此时,b就是a和b的公共祖先
//return a;
return res;
for(int i=M;i>=;i--)
{
if(a!=b && fa[a][i]!=fa[b][i])
{
res+=len[a][i];
a = fa[a][i];
res+=len[b][i];
b = fa[b][i];
}
} return res+len[a][]+len[b][];
}
int find(int a)
{
if(f[a]==a||f[a]==-)
return f[a]=a;
f[a]=find(f[f[a]]);
t[a]=t[f[a]];
return f[a];
}
int uni(int a,int b)
{
int x=find(a),y=find(b);
return f[y]=x;
}
bool issame(int a,int b)
{
return find(a)==find(b);
} bool cmp(int a,int b)
{
return d[a]>d[b];
}
void merg(int a,int b)
{
int fb=find(b);
long long l11=lca(t[a].x,t[fb].x);
long long l12=lca(t[a].x,t[fb].y);
long long l21=lca(t[a].y,t[fb].x);
long long l22=lca(t[a].y,t[fb].y);
long long maxl=max(max(max(max(max(l11,l12),l21),l22),t[a].r),t[fb].r);
int x1=t[a].x;
int x2=t[fb].x;
int y1=t[a].y;
int y2=t[fb].y;
if(l11==maxl)
{
t[a].x=x1;
t[a].y=x2;
}
if(l12==maxl)
{
t[a].x=x1;
t[a].y=y2;
}
if(l21==maxl)
{
t[a].x=y1;
t[a].y=x2;
}
if(l22==maxl)
{
t[a].x=y1;
t[a].y=y2;
}
if(t[a].r==maxl)
{
t[a].x=x1;
t[a].y=y1;
}
if(t[fb].r==maxl)
{
t[a].x=x2;
t[a].y=y2;
}
t[a].r=maxl;
f[fb]=a;
}
void put(int p)
{
t[p]=tree{,p,p};
f[p]=p;
for(int i=;i<(int)g[p].size();i++)
{
int to=g[p][i].to;
if(f[to]==-)
continue;
merg(p,to);
}
ans=max(ans,d[p]*t[p].r);
}
int main()
{
freopen("in.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
memset(ch,,sizeof(ch));
memset(fa,,sizeof(fa));
memset(len,,sizeof(len));
memset(deep,,sizeof(deep));
//scanf("%I64d",&n);
scanf("%lld",&n);
for(int i=;i<=n;i++)
g[i].clear();
for(int i=;i<=n;i++)
{
scanf("%lld",d+i);
a[i-]=i;
}
int x,y;
long long v;
for(int i=;i<n;i++)
{
//scanf("%d%d%I64d",&x,&y,&v);
scanf("%d%d%lld",&x,&y,&v);
g[x].push_back(node{y,v});
g[y].push_back(node{x,v});
}
dfs();
initFa(n);
sort(a,a+n,cmp);
memset(f,-,sizeof(f));
memset(t,,sizeof(t));
ans=-;
for(int i=;i<n;i++)
{
put(a[i]);
}
//printf("%I64d\n",ans);
printf("%lld\n",ans);
}
return ;
}
思路2:对点考虑, 对于当前点s(每次选取重心), 可以求出以它为根, 每一个子树上的点上到s的点权最小值, 和边权和, 然后 把每一个每一个子树分类, 这是为了避免计算的结果在同一个子树里。
然后计算不同子树中的结果, 这里计算结果时需要排个序, 按照点权最小值从大到小排序, 每次选取两个属于不同子树的最大边权和。
如此递归计算即可
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5+;
int siz[MAXN], n, val[MAXN];
bool center[MAXN]; typedef pair<int, int>pii;
struct Edge{
int to, next;
int c;
}e[MAXN * ];
int head[MAXN], edge_tot;
void Add_Edge(int x, int y, int z){
e[edge_tot].to = y;
e[edge_tot].next = head[x];
e[edge_tot].c = z;
head[x] = edge_tot++;
} void init (){
edge_tot = ;
memset(head, -, sizeof (head));
memset(center, , sizeof (center));
} pair <pair<long long, long long>, int>Mval[MAXN];
pii Find(int s, int pa, int tot) {
pii res = make_pair(INT_MAX, -);
int m = ;
siz[s] = ;
for (int i = head[s]; ~i; i = e[i].next) {
int u = e[i].to;
if (u == pa || center[u]) {
continue;
}
res = min(res, Find(u, s, tot));
siz[s] += siz[u];
m = max(m, siz[u]);
}
m = max(m, tot-siz[s]);
return min(res, make_pair(m, s));
} int idx, tim;
void Get_Min_Sum(int u, int pa, long long minval, long long sum){
Mval[idx++] = make_pair(make_pair(minval, sum), tim);
for (int i = head[u]; ~i; i = e[i].next){
int v = e[i].to;
if (v != pa && !center[v]){
Get_Min_Sum(v, u, min(minval, (long long)val[v]), sum+e[i].c);
}
}
}
long long sub_solve(){
sort (Mval, Mval+idx);
long long res = ;
long long sum1 = Mval[idx-].first.second, sum2 = ;
int t1 = Mval[idx-].second;
for (int i = idx-; i >= ; i--){
if (Mval[i].second != t1){
res = max(res, Mval[i].first.first*(Mval[i].first.second+sum1));
}else{
res = max(res, Mval[i].first.first*(Mval[i].first.second+sum2));
}
long long tmp = Mval[i].first.second;
if (tmp > sum1){
if (Mval[i].second == t1){
sum1 = tmp;
}else{
sum2 = sum1;
//t2 = t1;
sum1 = tmp;
t1 = Mval[i].second;
} }else{
if (tmp > sum2 && Mval[i].second != t1){
sum2 = tmp;
//t2 = Mval[i].second;
}
} }
return res;
}
long long solve (int u, int tot){
int g = Find(u, , tot).second;
center[g] = true;
long long res = ;
idx = ;
//tim++;
Mval[idx++] = make_pair(make_pair(val[g],), tim);
for (int i = head[g]; ~i; i = e[i].next){
int v = e[i].to;
int cost = e[i].c;
if (!center[v]){
tim++;
Get_Min_Sum(v, g, min(val[v], val[g]), cost);
}
}
res = max(res, sub_solve());
for (int i = head[g]; ~i; i = e[i].next){
int v = e[i].to;
if (!center[v]){
res = max(res, solve(v, siz[v]));
}
}
return res;
} int main()
{
//freopen("in.txt", "r", stdin);
int T;
scanf ("%d", &T);
while (T--){
init();
tim = ;
scanf ("%d", &n);
for (int i = ; i < n; i++){
scanf ("%d", val+i+);
}
for (int i = ; i < n-; i++){
int u, v, c;
scanf ("%d%d%d", &u, &v, &c);
Add_Edge(u, v, c);
Add_Edge(v, u, c);
}
long long res = solve(, n);
printf("%lld\n", res); }
return ;
}
计蒜客 444 / xtuoj 1024 京东的物流路径(并查集+离线lca)或者 (点分治)的更多相关文章
- 2018 计蒜之道复赛 贝壳找房魔法师顾问(并查集+dfs判环)
贝壳找房在遥远的传奇境外,找到了一个强大的魔法师顾问.他有 22 串数量相同的法力水晶,每个法力水晶可能有不同的颜色.为了方便起见,可以将每串法力水晶视为一个长度不大于 10^5105,字符集不大于 ...
- 2019icpc徐州站 Cat 计蒜客 - 42540 && The Answer to the Ultimate Question of Life, The Universe, and Everything. 计蒜客 - 42545
VJ链接:https://vjudge.net/contest/412095#problem/A Cat 计蒜客 - 42540 题意: 给你一个区间[L,R],给你现在拥有的钱S.你需要从[L,R] ...
- 计蒜客 作弊揭发者(string的应用)
鉴于我市拥堵的交通状况,市政交管部门经过听证决定在道路两侧安置自动停车收费系统.当车辆驶入车位,系统会通过配有的摄像头拍摄车辆画面,通过识别车牌上的数字.字母序列识别车牌,通过连接车管所车辆信息数据库 ...
- 计蒜客的一道题dfs
这是我无聊时在计蒜客发现的一道题. 题意: 蒜头君有一天闲来无事和小萌一起玩游戏,游戏的内容是这样的:他们不知道从哪里找到了N根不同长度的木棍, 看谁能猜出这些木棍一共能拼出多少个不同的不等边三角形. ...
- 计蒜客模拟赛5 D2T1 成绩统计
又到了一年一度的新生入学季了,清华和北大的计算机系同学都参加了同一场开学考试(因为两校兄弟情谊深厚嘛,来一场联考还是很正常的). 不幸的是,正当老师要统计大家的成绩时,世界上的所有计算机全部瘫痪了. ...
- 计蒜客 等边三角形 dfs
题目: https://www.jisuanke.com/course/2291/182238 思路: 1.dfs(int a,int b,int c,int index)//a,b,c三条边的边长, ...
- 计蒜客 方程的解数 dfs
题目: https://www.jisuanke.com/course/2291/182237 思路: 来自:https://blog.csdn.net/qq_29980371/article/det ...
- 计蒜客 买书 dfs
题目: https://www.jisuanke.com/course/2291/182236 思路: 递归解决,从第一本书开始,每本书都有两种选择: //index是book里面每本书价格的下标, ...
- 计蒜客:Entertainment Box
Ada, Bertrand and Charles often argue over which TV shows to watch, and to avoid some of their fight ...
随机推荐
- JavaScripts学习日记——XML DTD Schema
今日关键词: XML DTD Schema 1.XML 1 XML的概述 1.1 什么是XML XML全称为Extensible Markup Language,意思是可扩展的标记语言.XML语法上和 ...
- mongodb创建副本集命令
mongodb创建副本集命令 ./mongod --replSet spock --dbpath ../data --smallfiles > config ={... "_id&qu ...
- How to Build CyanogenMod for One X (codename: endeavoru)
来源:http://wiki.cyanogenmod.org/w/Build_for_endeavoru#What_you.E2.80.99ll_need How to Build CyanogenM ...
- 无法连接vCenter Server清单https://IP:10443
VMware vCenter Server服务器安装系统的时候使用一个IP,安装完VMware vCenter后来更换了另外一个IP,当使用vSphere Web Client登陆VMware vCe ...
- Cacti添加IO模板并监控磁盘IO
1.下载Cacti_Net-SNMP_DevIO_v3.1.zip 下载Cacti_Net-SNMP_DevIO_v3.1.zip,解压并上传net-snmp_devio.xml到/resource/ ...
- iOS UI_APPEARANCE_SELECTOR
iOS后属性带UI_APPEARANCE_SELECTOR 可以统一设置全局作用 例如: 1>开关控件 @property(nullable, nonatomic, strong) UIColo ...
- IE9的BUG?jQuery的BUG?
本文转载自http://big-student.iteye.com/blog/1898213 在IE9和IE10中,当对一个html的样式初始了一个很大的left或者top时,使用jQuery的off ...
- C# 中显示实现接口
接口的实现分为显示实现和隐式实现 用显示实现接口的目的就是为了,当一个类中实现多个具有相同方法的接口时,能够区分开来 在调用的时候,必须用接口调用. class Program { static vo ...
- iOS中常用的四种数据持久化方法简介
iOS中常用的四种数据持久化方法简介 iOS中的数据持久化方式,基本上有以下四种:属性列表.对象归档.SQLite3和Core Data 1.属性列表涉及到的主要类:NSUserDefaults,一般 ...
- hdu 2992 Hotel booking
http://acm.hdu.edu.cn/showproblem.php?pid=2992 #include <cstdio> #include <cstring> #inc ...