[Tricks-00006]CF1558E 如何处理无向图中的任意环?tourist 题,太神啦。
题意:
自己看去。不过有个限制别忘了:每个点的度数都至少为 \(\geq 2\)。
我写这些 Trick 题解还是要说清思考方法。不过这个题确实有点难以观察到了/ll
还是从简单到难地去讲吧:
第一件事。如果没有后面那个不能返回的条件的限制。那么其实可能有很多种想法,不过大体思路都是统一的:每次加一个点,然后马上回到根,再加一个点,再回来。都只在已经扩展到的集合上加上一个点,或者说类似 Prim 的东西。
好了,说了一堆废话,回到正题。
这个题一定也是一步一步扩展下去的,不难发现。可以扩展出什么样子的呢?注意到并不一定必须要是边双才行,有这样的路径:

一个天真的想法是,每次加一个这东西,下次再加,每次都加这样形状物。不过,我在思考本题的时候似乎遇到了一个问题:
如果直接走了蓝链回根,如果下一步刚好(且只能)要走刚返回的这条边出去,咋办啊?似乎就多了个限制,不能直接加了,难过。
不过仔细想完之后,其实得到这样一个调整:
每次走一条边会堆在一个栈顶上,如果要回去,直接 pop,就好啦!
这是你重新回去想想,只要遇到相同,都这么做不香吗?其实你发现,只要弹出的是已访问的点,就不会有错!!!
因此我们就可以理清楚思路了!每次加一个不返回的路径,只要碰到一个访问过的点,就结束了,之后怎么在已访问的窝里"横"都行,大家都一家亲。
然后加的路径也要对应地修改下了。其实也未必是个环,就走到一个已经访问到的点就 ok 了!
很好,目前有了个很不错的思路。画出图长这样:

其实,用心感受,加的是一个"耳朵"(不过和耳分解里的耳是有区别的,这里又不是边双),起始点和终止点都是已经访问过的,中间不经过同一节点(经过了就应该在这里直接加了),然后把这段路径的所有点的访问标记标 \(1\)。
先二分初始的钱数,每次能加一个就加一个,这样至少已经有了个看着就是 poly 复杂度的做法。但是怎么进一步优化呢?
看眼瓶颈在哪里。二分一个 \(\log\),至多会添加 \(n\) 轮,每轮寻找的复杂度会有小问题。一种想法是 dijkstra,跑一个最短路。这个有人实现了,复杂度没问题,不过代码难度会大一些。
还有这样一个做法:还是去想那个找"环"的过程。刚才的思路类似 bfs 的东西,这次我们沿 dfs 的思路去考虑:
从一个已访问点开始走,进行依次遍历,只要能走就去走。直到能回到一个已访问的点为止。
这是个没错的思路,不过复杂度有点假。那么我们必须保证:每个未访问的点至多被遍历到一次。
要满足这个事情,就必须考虑到一个点被遍历到两次的后果。找到最开始出现这种情况的点,因为没重边,所以开始时每个点都至多遍历第一次。当重复遇到一个点时,有下列两种情况:

其中第一种直接转就可以了。第二种你仔细想想,因为是第一次遇到一个遍历两次的点,所以上一步肯定不同,把边换方向。选 \(\sum b\) 较大的那条路先走,然后再走回去,也一定行!因此,第二次遇到一个点时,一定是直接可以得到答案的。
这样,直接 dfs 遇到重复点或已访问点的时候找出路径就 ok 了。时间复杂度 \(O(nm\log a)\)。
代码不算很难写:
#include<bits/stdc++.h>
using namespace std;
void gai(int x,int p,long long d);
void dfs(int x);
int n,m,a[1005],b[1005];
vector<int>g[1005];
int vist[1005],pre[1005];
long long H,dist[1005];
bool fl;
void gai(int x,int p,long long d){
if(!pre[x]){
pre[x]=p;dist[x]=d;
dfs(x);
}else{
while(!vist[x]){
vist[x]=1;H+=b[x];
x=pre[x];
}
while(!vist[p]){
vist[p]=1;H+=b[p];
p=pre[p];
}
fl=1;
}
}
void dfs(int x){
for(auto y:g[x])if(y!=pre[x]){
if(fl)continue;
if(vist[y]){
while(!vist[x]){
vist[x]=1;H+=b[x];
x=pre[x];
}
fl=1;return;
}
if(H+dist[x]<=a[y])continue;
gai(y,x,dist[x]+b[y]);
}
}
bool ok(int x){
H=x;
for(int i=1;i<=n;++i)vist[i]=0;
vist[1]=1;
while(1){
for(int i=1;i<=n;++i)pre[i]=0;
fl=0;
for(int i=1;i<=n&&!fl;++i)if(vist[i]){
for(auto j:g[i])if(!vist[j]){
if(H<=a[j])continue;
if(!fl)gai(j,i,b[j]);
}
}
if(!fl)break;
}
for(int i=1;i<=n;++i)if(!vist[i])return 0;
return 1;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
int mx=0;
for(int i=2;i<=n;++i)scanf("%d",&a[i]),mx=max(mx,a[i]);
for(int i=2;i<=n;++i)scanf("%d",&b[i]);
for(int i=1;i<=n;++i){
g[i].clear();
}
for(int i=1;i<=m;++i){
int u,v;
scanf("%d%d",&u,&v);
g[u].emplace_back(v);
g[v].emplace_back(u);
}
int L=0,R=mx;
while(L<=R){
int mid=(L+R)>>1;
if(!ok(mid))L=mid+1;
else R=mid-1;
}
printf("%d\n",L);
}
return 0;
}

浙公网安备 33010602011771号