problem
Description
为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为1,2,3, … , n,边标号为 1,2,3, … , m。初始时小 E 同学在 1 号节点,隐士则住在 n 号节点。小 E 需要通过这一片魔法森林,才能够拜访到隐士。
魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击。 幸运的是, 在 1 号节点住着两种守护精灵: A 型守护精灵与B 型守护精灵。小 E 可以借助它们的力量,达到自己的目的。
只要小 E 带上足够多的守护精灵, 妖怪们就不会发起攻击了。具体来说, 无向图中的每一条边 ei 包含两个权值 ai 与 bi 。 若身上携带的 A 型守护精灵个数不少于 ai ,且 B 型守护精灵个数不少于 bi ,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向小 E 发起攻击,他才能成功找到隐士。
由于携带守护精灵是一件非常麻烦的事,小 E 想要知道, 要能够成功拜访到隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为 A 型守护精灵的个数与 B 型守护精灵的个数之和。
Input
输入文件的第 1 行包含两个整数 n, m,表示无向图共有 n 个节点, m 条边。
接下来 m 行,第 i + 1 行包含 4 个正整数 Xi, Yi, ai, bi, 描述第 i 条无向边。其中Xi与Yi为该边两个端点的标号,ai与bi的含义如题所述。
注意数据中可能包含重边与自环。
Output
输出一行一个整数:如果小 E 可以成功拜访到隐士,输出小 E 最少需要携带的守护精灵的总个数;如果无论如何小 E 都无法拜访到隐士,输出“-1” (不含引号) 。
Sample Input
【样例输入 1】
4 5
1 2 19 1
2 3 8 12
2 4 12 15
1 3 17 8
3 4 1 17
【样例输入 2】
3 1
1 2 1 1
Sample Output
【样例输出 1】
32
【样例输出 2】
-1
Data Constraint
Hint
【样例说明 1】
如果小 E 走路径 1→2→4,需要携带 19+15=34 个守护精灵;
如果小 E 走路径 1→3→4,需要携带 17+17=34 个守护精灵;
如果小 E 走路径 1→2→3→4,需要携带 19+17=36 个守护精灵;
如果小 E 走路径 1→3→2→4,需要携带 17+15=32 个守护精灵。
综上所述,小 E 最少需要携带 32 个守护精灵。
【样例说明 2】
小 E 无法从 1 号节点到达 3 号节点,故输出-1。
analysis
好像MST的样子,但思考一下就可以发现kruskal是错的
正解LCT,但听说SPFA也可AC?
维护动态增删边MST就可以了
注意此处维护边权,就按平常套路,把边看成点连起来,边权看成点权
首先将aa升序排序,保证最优,接下来维护bb的MST,怎么弄呢?
我们重新定义一下,为ii节点子树内权值最大的点的标号
如果当前第条边的bb小于当前LCT里面最大的边,我们不就可以把这条边换掉了?
通过查找querymax(x,y)querymax(x,y)来查找并切断那一条较劣的边即可
然后中途只要11和是联通的,就统计答案ans=min(f[i].a+a[querymax(1,n)].val)ans=min(f[i].a+a[querymax(1,n)].val)
搞定
code
#include<bits/stdc++.h>
#define MAXN 200001
#define MAXM 500001
#define INF 1000000007
using namespace std;
int t[MAXN][2];
int fat[MAXN],fa[MAXN],pf[MAXN],st[MAXN];
char s[10];
int n,m,ans;
struct node
{
int val,mx,size;
bool rev;
}a[MAXN];
struct edge
{
int x,y,a,b;
}f[MAXM];
bool cmp(edge x,edge y)
{
return x.a<y.a;
}
int getfa(int x)
{
return !fat[x]?x:fat[x]=getfa(fat[x]);
}
int read()
{
int x=0,f=1;
char ch=getchar();
while (ch<'0' || '9'<ch)
{
if (ch=='-')f=-1;
ch=getchar();
}
while ('0'<=ch && ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
void reverse(int x)
{
if(x)
{
a[x].rev^=1;
swap(t[x][0],t[x][1]);
}
}
void down(int x)
{
if (a[x].rev)
{
reverse(t[x][0]),reverse(t[x][1]);
a[x].rev=0;
}
}
void update(int x)
{
if (x)
{
a[x].size=a[t[x][0]].size+a[t[x][1]].size+1;
a[x].mx=x;
if (a[a[t[x][0]].mx].val>a[a[x].mx].val)a[x].mx=a[t[x][0]].mx;
if (a[a[t[x][1]].mx].val>a[a[x].mx].val)a[x].mx=a[t[x][1]].mx;
}
}
void downdata(int x)
{
st[0]=0;
while (x)st[++st[0]]=x,x=fa[x];
while (st[0])down(st[st[0]--]);
}
int lr(int x)
{
return t[fa[x]][1]==x;
}
void rotate(int x)
{
int y=fa[x],k=lr(x);
t[y][k]=t[x][!k];
if (t[x][!k])fa[t[x][!k]]=y;
fa[x]=fa[y];
if (fa[y])t[fa[y]][lr(y)]=x;
t[x][!k]=y;
fa[y]=x,pf[x]=pf[y];
update(y),update(x);
}
void splay(int x, int y)
{
downdata(x);
while (fa[x]!=y)
{
if (fa[fa[x]]!=y)
{
if (lr(x)==lr(fa[x]))rotate(fa[x]);
else rotate(x);
}
rotate(x);
}
}
void access(int x)
{
for (int y=0;x;update(x),y=x,x=pf[x])
{
splay(x,0);
fa[t[x][1]]=0;
pf[t[x][1]]=x;
t[x][1]=y;
fa[y]=x;
pf[y]=0;
}
}
void makeroot(int x)
{
access(x);
splay(x,0);
reverse(x);
}
int getroot(int x)
{
while (fa[x])x=fa[x];
return x;
}
bool judge(int x,int y)
{
makeroot(x);
access(y);
splay(x,0);
return getroot(y)==x;
}
void link(int x,int y)
{
makeroot(x);
pf[x]=y;
}
void cut(int x,int y)
{
makeroot(x);
access(y);
splay(x,0);
t[x][1]=fa[y]=pf[y]=0;
update(x);
}
int query_max(int x,int y)
{
makeroot(y);
access(x);
splay(x,0);
return a[x].mx;
}
int main()
{
n=read(),m=read();
for (int i=1;i<=m;i++)f[i].x=read(),f[i].y=read(),f[i].a=read(),f[i].b=read();
sort(f+1,f+m+1,cmp);
ans=INF;
for (int i=1;i<=m;i++)
{
int x=f[i].x,y=f[i].y;
if (getfa(x)==getfa(y))
{
int temp=query_max(x,y);
if (f[i].b<a[temp].val)cut(temp,f[temp-n].x),cut(temp,f[temp-n].y);
else
{
if (getfa(1)==getfa(n))ans=min(ans,f[i].a+a[query_max(1,n)].val);
continue;
}
}
else fat[getfa(x)]=getfa(y);
link(x,n+i),link(y,n+i);
a[n+i].mx=n+i,a[n+i].val=f[i].b;
if (getfa(1)==getfa(n))ans=min(ans,f[i].a+a[query_max(1,n)].val);
}
(ans==INF)?(printf("-1\n")):(printf("%d\n",ans));
return 0;
}

小 E 为了拜访魔法森林中的隐士,需要通过一个由守护精灵保护的无向图。每条边需要一定数量的A型和B型精灵避免妖怪攻击。问题转化为找出从1到n的最小守护精灵总和,可以使用LCT(线段树)或动态维护MST解决。样例解释了不同路径的精灵需求,并指出当无法到达隐士时输出-1。
776

被折叠的 条评论
为什么被折叠?



