版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/88898453
title
analysis
-
30 分做法
对于 30% 的数据, 。
暴力用 算法求出最小生成森林即可。
时间复杂度 。 -
60 分做法
对于 60% 的数据,边权随编号递增。
注意到边权随编号递增,因此从大到小依次加入每条边。
每加入一条边时,如果没有成环,那么它就是树边,否则要
切掉环上最大的那条边,再连上这条边。
那么询问 的答案就是加入了所有 的边后,编号不
超过 的树边的权值之和。
对于树结构的维护可以暴力,对于权值和的询问可以用树状
数组维护。
时间复杂度 。 -
100 分做法
考虑用线段树直接维护每个区间的答案。
注意到一个区间最多只有 条树边有用,所以线段树每
个节点按权值从小到大保存区间内用到的树边即可。
合并两个区间的信息时,只需要将树边归并,然后做
算法。
时间复杂度 。
code
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
struct mst
{
int x,y,z,id;
inline bool operator < (const mst &a) const
{
return z<a.z;
}
}e[maxn],tmp[maxn];
struct sgt
{
int pos[105];//记录地址
int cnt;//选择边的数量
inline void insert(int x)
{
pos[++cnt]=x;
}
}tree[maxn<<2],Stack[maxn];
///////////////////////////////////////////////////////////////Here is MST
int fa[maxn],top,cnt,n,m,q;
inline int get(int x)
{
return fa[x]==x?x:fa[x]=get(fa[x]);
}
inline sgt merge(sgt x,sgt y)
{
sgt z;
z.cnt=0;
cnt=0;
for (int i=1; i<=x.cnt; ++i)//将两个节点合并
tmp[++cnt]=e[x.pos[i]];
for (int i=1; i<=y.cnt; ++i)
tmp[++cnt]=e[y.pos[i]];
sort(tmp+1,tmp+cnt+1);//跑一遍Kruskal
for (int i=1; i<=n; ++i)
fa[i]=i;
for (int i=1; i<=cnt; ++i)
{
int x=get(tmp[i].x),y=get(tmp[i].y);
if (x==y) continue;
fa[x]=y;
z.pos[++z.cnt]=tmp[i].id;//所选取的较优秀的边,记录下他的地址
}
return z;
}
///////////////////////////////////////////////////////////////Above is MST
///////////////////////////////////////////////////////////////Here is SGT
inline void build(int now,int l,int r)
{
if (l==r)
{
tree[now].insert(l);
return ;
}
int mid=(l+r)>>1;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
tree[now]=merge(tree[now<<1],tree[now<<1|1]);//同上
}
inline void ask(int now,int l,int r,int tl,int tr)
{
if (tl<=l && r<=tr)
{
Stack[++top]=tree[now];//加入答案集合中
return ;
}
int mid=(l+r)>>1;
if (tl<=mid)
ask(now<<1,l,mid,tl,tr);
if (tr>mid)
ask(now<<1|1,mid+1,r,tl,tr);
}
///////////////////////////////////////////////////////////////Above is SGT
inline int work(int l,int r)
{
top=0;
ask(1,1,m,l,r);
for (int i=2; i<=top; ++i)
Stack[1]=merge(Stack[1],Stack[i]);
int ans=0;
for (int i=1; i<=Stack[1].cnt; ++i)
ans+=e[Stack[1].pos[i]].z;
return ans;
}
int main()
{
read(n);read(m);read(q);
for (int i=1; i<=m; ++i)
{
read(e[i].x);read(e[i].y);read(e[i].z);
e[i].id=i;
}
build(1,1,m);
while (q--)
{
int l,r;
read(l);read(r);
printf("%d\n",work(l,r));
}
return 0;
}
summary
嗯,是真没看出来三十分暴力可以写最小生成树,哎,越发乎觉得知识点掌握的不够熟练,都已经想到了暴力删边求求树直径了,为什么不往下再想一步呢,对自己也是很失望,马上要省选了,虽然我是去打酱油的,但是也想漂亮的回来,这样子怎么行?哎,只能继续努力了。。。。