HDU - 4424 Conquer a New Region 思维+并查集启发式合并
2017-09-30 22:57
489 查看
题目链接
题意:
给n个城镇,然后每两个城镇的价值给出,但是u->v的价值是u->v这条路径上的最小值,问从任意一个点出发到其他位置的和的最大值
思路:
很好的一个并查集题目,和队友想了一会才想出来.说实话这个并查集不是很好想。
因为这个题目是路径上的最小值为sij,所以我们要将边的权值从大到小排序,这样在用并查集维护两堆合并的最优值时,假设祖先分别为f1和f2,首先内部我们已经处理好了(每次合并就已经处理好),因为从大到小排序的,那么我们现在维护的这条边一定是当前最小的,那么我们以祖先f1到另一堆的所有点的sij即为当前这条边的cij,而到自己这一堆我们的已经处理好了为sum[f1]。(以f2同理)
关键是合并的时候怎么合并,因为要求和的最大值,所以这里我们用num[i]表示该堆的点的个数,sum[i]表示该堆,以i为选定的点,到本堆其他点的和的最大值是多少,合并时 f1,f2两堆,如果num[f2]*w+sum[f1]大,则新堆以f1为选定点,如果num[f1]*w+sum[f2]大则合并到f2上,这个过程中维护一个最大值即可》
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
typedef long long ll;
int n;
int pre[maxn],num[maxn];
ll sum[maxn],ans;
void init()
{
for(int i = 0;i <= n;++i)
pre[i] = i,sum[i] = 0,num[i] = 1;
}
struct node
{
int u,v,w;
bool operator<(const node & z) const
{
return w > z.w;
}
}a[maxn];
int find(int x)
{
return x == pre[x] ? x : pre[x] = find(pre[x]);
}
void join(int x,int y,int w)
{
int f1 = find(x),f2 = find(y);
ll s1 = sum[f1] + (ll) w * num[f2];
ll s2 = sum[f2] + (ll) w * num[f1];
if(s1 >= s2)
{
pre[f2] = f1;
sum[f1] = s1;
num[f1] += num[f2];
}
else
{
pre[f1] = f2;
sum[f2] = s2;
num[f2] += num[f1];
}
ans = max(ans,max(sum[f1],sum[f2]));
return ;
}
int main()
{
while(~scanf("%d",&n))
{
init();
for(int i = 1;i < n;++i)
scanf("%d %d %d",&a[i].u,&a[i].v,&a[i].w);
sort(a+1,a+n);
ans = 0;
for(int i = 1;i < n;++i)
{
join(a[i].u,a[i].v,a[i].w);
}
printf("%lld\n",ans);
}
return 0;
}
题意:
给n个城镇,然后每两个城镇的价值给出,但是u->v的价值是u->v这条路径上的最小值,问从任意一个点出发到其他位置的和的最大值
思路:
很好的一个并查集题目,和队友想了一会才想出来.说实话这个并查集不是很好想。
因为这个题目是路径上的最小值为sij,所以我们要将边的权值从大到小排序,这样在用并查集维护两堆合并的最优值时,假设祖先分别为f1和f2,首先内部我们已经处理好了(每次合并就已经处理好),因为从大到小排序的,那么我们现在维护的这条边一定是当前最小的,那么我们以祖先f1到另一堆的所有点的sij即为当前这条边的cij,而到自己这一堆我们的已经处理好了为sum[f1]。(以f2同理)
关键是合并的时候怎么合并,因为要求和的最大值,所以这里我们用num[i]表示该堆的点的个数,sum[i]表示该堆,以i为选定的点,到本堆其他点的和的最大值是多少,合并时 f1,f2两堆,如果num[f2]*w+sum[f1]大,则新堆以f1为选定点,如果num[f1]*w+sum[f2]大则合并到f2上,这个过程中维护一个最大值即可》
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
typedef long long ll;
int n;
int pre[maxn],num[maxn];
ll sum[maxn],ans;
void init()
{
for(int i = 0;i <= n;++i)
pre[i] = i,sum[i] = 0,num[i] = 1;
}
struct node
{
int u,v,w;
bool operator<(const node & z) const
{
return w > z.w;
}
}a[maxn];
int find(int x)
{
return x == pre[x] ? x : pre[x] = find(pre[x]);
}
void join(int x,int y,int w)
{
int f1 = find(x),f2 = find(y);
ll s1 = sum[f1] + (ll) w * num[f2];
ll s2 = sum[f2] + (ll) w * num[f1];
if(s1 >= s2)
{
pre[f2] = f1;
sum[f1] = s1;
num[f1] += num[f2];
}
else
{
pre[f1] = f2;
sum[f2] = s2;
num[f2] += num[f1];
}
ans = max(ans,max(sum[f1],sum[f2]));
return ;
}
int main()
{
while(~scanf("%d",&n))
{
init();
for(int i = 1;i < n;++i)
scanf("%d %d %d",&a[i].u,&a[i].v,&a[i].w);
sort(a+1,a+n);
ans = 0;
for(int i = 1;i < n;++i)
{
join(a[i].u,a[i].v,a[i].w);
}
printf("%lld\n",ans);
}
return 0;
}
相关文章推荐
- hdu 4677 并查集合并(两个相邻区间并查集的合并)+分块算法 好题
- BZOJ.3673/3674.可持久化并查集(可持久化线段树 按秩合并/启发式合并)
- 2017 广西邀请赛&& hdu 6191 Query on A Tree(字典树启发式合并)
- HDU 3461 Code Lock(并查集,合并区间,思路太难想了啊)
- CDOJ1927 爱吃瓜的伊卡洛斯(2) 【并查集】启发式合并+set
- 【bzoj4537】【HNOI2016】【最小公倍数】【并查集+启发式合并+分块】
- HDU - 6191 Query on A Tree 可持久化字典树(01Trie) || 字典树启发式合并
- hdu Junk-Mail Filter 并查集的合并与删除
- 可持久化并查集(外传)——[按秩启发式合并]
- hdu 6191 可持久化trie||线段树套trie||trie启发式合并
- hdu 4670 Cube number on a tree,平衡树,启发式合并
- JZOJ5373. 【NOIP2017提高A组模拟9.17】信仰是为了虚无之人 并查集+启发式合并
- CodeForces 827D Best Edge Weight (倍增 启发式合并 链剖 并查集)
- HDU 6133 Army Formations 树状数组 + 启发式合并
- HDU 5997 rausen loves cakes (线段树区间维护,启发式区间合并)
- 【HDU】5197 DZY Loves Orzing 【FFT启发式合并】
- 并查集的优化---路径压缩与启发式合并
- HDU - 6133 Army Formations(启发式合并+树状数组)
- [后缀数组][trie合并][启发式合并][并查集] LOJ #6198. 谢特
- COCI 2017/2018 Round #3,November 25th,2017 Pictionary [带权并查集+启发式合并]