GDOI2018 day2 巡逻

和标算不同的做法。
先建立最短路树。
引理:最多只会经过一条非树边。
画图会发现是正确的
建立源点开始的最短路树,把源点的每一个子树染色。
枚举每条边,如果两端点颜色不同,则统计答案。
时间复杂度(O(n^3))

#include<bits/stdc++.h>
using namespace std;
#define N 410
int n,q,a[N][N],d[N],f[N],cl[N],h[N],v[N*2],nxt[N*2],dd[N],ec,w[N*2],vi[N],vv[N];
void add(int x,int y,int z){
	v[++ec]=y;
	w[ec]=z;
	nxt[ec]=h[x];
	h[x]=ec;
}
void dfs(int x,int fa,int c){
	cl[x]=c;
	for(int i=h[x];i;i=nxt[i])
		if(v[i]!=fa){
			dd[v[i]]=dd[x]+w[i];
			dfs(v[i],x,c);
		}
}
void dj(int s){
	memset(v,0,sizeof(v));
	memset(nxt,0,sizeof(nxt));
	memset(h,0,sizeof(h));
	memset(cl,0,sizeof(cl));
	ec=0;
	for(int i=0;i<=n;i++){
		d[i]=1e7;
		vv[i]=f[i]=dd[i]=cl[i]=0;
	}
	d[s]=0;
	for(int i=1;i<=n;i++){
		int t=0;
		for(int j=1;j<=n;j++)
			if(d[t]>d[j]&&!vv[j]&&vi[j])
				t=j;
		vv[t]=1;
		if(!t)
			break;
		for(int j=1;j<=n;j++)
			if(j!=t&&d[j]>d[t]+a[t][j]&&!vv[j]&&vi[j]){
				f[j]=t;
				d[j]=d[t]+a[t][j];
			}
	}
	for(int i=1;i<=n;i++)
		if(i!=s&&vi[i]&&vi[f[i]])
			add(f[i],i,a[f[i]][i]);
	int c=0;
	for(int i=1;i<=n;i++)
		if(i!=s&&vi[i]&&!cl[i]){
			dd[i]=a[s][i];
			dfs(i,s,++c);
		}
}
int main(){
	freopen("patrol.in","r",stdin);
	freopen("patrol.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			scanf("%d",&a[i][j]);
			if(a[i][j]==-1)
				a[i][j]=1e7;
		}
		vi[i]=1;
	}
	scanf("%d",&q);
	while(q--){
		int op,x;
		scanf("%d%d",&op,&x);
		if(op)
			vi[x]^=1;
		else{
			dj(x);
			int ans=1e8;
			for(int i=1;i<=n;i++)
				for(int j=1;j<=n;j++)
					if(vi[i]&&vi[j]&&cl[i]!=cl[j]&&f[j]!=i&&f[i]!=j)
						ans=min(ans,a[i][j]+dd[i]+dd[j]);
			if(ans>=1e7)
				puts("-1");
			else
				printf("%d
",ans);
		}
	}
}
原文地址:https://www.cnblogs.com/ctmlpfs/p/14503544.html