【题解】NOI2014 魔法森林

【题解】NOI2014 魔法森林

十二月 27, 2020

题目大意:

从 $s$ 到 $t$ 找到一条路径 $S$,使得 $\max_{i\in S}a_i+\max_{i\in S}b_i$ 最小。

做的时候被那个 5e4 整晕了。

所以直接就用分块做这个题了。

考虑枚举最小的 $a_i$,不难发现这个时候最小的 $b_i$ 随 $a_i$ 的增大单调不增,所以是可以双指针的。

考虑使用并查集来判断当前答案的合法性,既考虑将所有满足条件的边连完后 $s$ 和 $t$ 是否连通,不难想到用分块优化复杂度。

即每次将零散块部分暴力插入并查集再撤销。

复杂度为 $O(m\sqrt{n\log_2n})$。

实际上由于数据较水所以跑得很快。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define rg register
//#define int long long
il int read()
{
int re=0,k=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')k=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){re=re*10+ch-48;ch=getchar();}
return re*k;
}
il void write(int x)
{
if(x<0)return putchar('-'),write(-x),void();
if(x<10)return putchar(x+'0'),void();
return write(x/10),putchar(x%10+'0'),void();
}
const int B=800;
int n,m,ans;
struct ss{
int u,v,a,b;
bool operator <(const ss ot)const{
return a<ot.a;
}
}e[10000005];
struct st{
int u,v,b;
bool operator <(const st ot)const{
return b<ot.b;
}
}now[10000005];
int fa[10000005],sz[10000005],s[10000005],ls,lnow,las;
bool vis[10000005];
void merge(int u,int v)
{
if(u==v)return;
if(sz[u]>sz[v])swap(u,v);
fa[u]=v;
sz[v]+=sz[u];
s[++ls]=u;
}
int find(int x)
{
if(x==fa[x])return x;
return find(fa[x]);
}
void redo()
{
sz[fa[s[ls]]]-=sz[s[ls]];
fa[s[ls]]=s[ls];
ls--;
}
bool pd(int x)
{
redo();
lnow--;
int t=ls;
for(rg int i=las;i<=x;i++)
{
if(e[i].b>now[lnow].b)continue;
merge(find(e[i].u),find(e[i].v));
}
if(find(n)==find(1)){while(ls>t)redo();return 1;}
while(ls>t)redo();
lnow++;
merge(find(now[lnow].u),find(now[lnow].v));
return 0;
}
void init()
{
for(rg int i=1;i<=n;i++)
fa[i]=i,sz[i]=1;
}
void build()
{
init();
for(rg int i=1;i<=lnow;i++)
{
if(find(now[i].u)!=find(now[i].v))vis[i]=1;
else vis[i]=0;
merge(find(now[i].u),find(now[i].v));
}
}
signed main()
{
n=read();m=read();
for(rg int i=1;i<=m;i++)
{
e[i].u=read();e[i].v=read();
e[i].a=read();e[i].b=read();
}
sort(e+1,e+m+1);
init();
ans=0x3f3f3f3f;
int st=1;
init();
while(find(1)!=find(n)&&st<=m)
{
now[++lnow].u=e[st].u;
now[lnow].v=e[st].v;
now[lnow].b=e[st].b;
merge(find(e[st].u),find(e[st].v));
st++;
}
sort(now+1,now+lnow+1);
build();
las=st;
do
{
while(!vis[lnow]&&lnow)
lnow--;
}
while(pd(st-1));
if(find(1)==find(n))
{
ans=e[st-1].a+now[lnow].b;
}
for(rg int i=st;i<=m&&lnow>0;i++)
{
do
{
while(!vis[lnow]&&lnow)
lnow--;
}
while(pd(i));
if(lnow>0)
ans=min(ans,e[i].a+now[lnow].b);
if(i-las+1==B)
{
int t=lnow;
for(rg int j=las;j<=i;j++)
{
if(e[j].b>now[t].b)continue;
now[++lnow].u=e[j].u;
now[lnow].v=e[j].v;
now[lnow].b=e[j].b;
}
las=i+1;
sort(now+1,now+lnow+1);
build();
}
}
if(ans==0x3f3f3f3f)
ans=-1;
write(ans);
}