把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P5236 【模板】静态仙人掌

题面传送门
一道圆方树的板子题。
对于每个点先让它连到环顶。
那么他到环顶的距离就是从两边走的最小值。
那么求两点间距离就可以考虑倍增了。
但是最后要考虑特殊情况
代码实现:

#include<cstdio>
#include<cstring>
#define abs(x) ((x)>0?(x):-(x))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,k,xs,y,z,fa[10039][20],lg[10039],d[10039],now,cur,tot,ans[10039],vis[10039],st[10039],sh,f1[10039],f2[10039],flag[10039],nows[10039],nh;
long long sum[10039][20];
struct yyy{int to,w,z;}tmp;
struct ljb{
	int head,h[10039];
	yyy f[40039];
	inline void add(int x,int y,int z){
		f[++head]=(yyy){y,z,h[x]};
		h[x]=head;
	}
}s,c;
inline void dfs(int x,int last){
	int cur=s.h[x];
	yyy tmp;
	st[++sh]=x;vis[x]=1;
	while(cur!=-1){
		tmp=s.f[cur];
		if(tmp.to!=last){
		    if(vis[tmp.to]){
		    	if(!ans[tmp.to]){
		    		tot++;nh=0;
			        while(st[sh]!=tmp.to&&sh){
			        	c.add(tmp.to,st[sh],0);
			        	//printf("%d %d\n",tmp.to,st[sh]);
			        	ans[st[sh]]=tot;
			        	if(st[sh]==x)f1[x]=tmp.w;
			        	else f1[st[sh]]+=f1[st[sh+1]];
			        	nows[++nh]=st[sh];
			        	sh--;
			        }
			        nh--;
			        while(nh){
				        f2[nows[nh]]+=f2[nows[nh+1]];
			        	nh--;
		    	    }
				}
		    }
		    else{
		    	if(!ans[x]) f1[x]=tmp.w;
		    	f2[tmp.to]=tmp.w;
		    	dfs(tmp.to,x);
		    }
		}
		cur=tmp.z;
	}
	if(!ans[x]) sh--,f1[x]=f2[x],c.add(last,x,0)/*,printf("%d %d\n",last,x)*/;
}
inline void dfss(int x,int last){
	fa[x][0]=last;sum[x][0]=min(f1[x],f2[x]);
	d[x]=d[last]+1;
	for(int k=1;k<=lg[d[x]];k++)sum[x][k]=sum[fa[x][k-1]][k-1]+sum[x][k-1],fa[x][k]=fa[fa[x][k-1]][k-1];
	int cur=c.h[x];
	yyy tmp;
	while(cur!=-1){
		tmp=c.f[cur];
		if(tmp.to!=last) dfss(tmp.to,x);
		cur=tmp.z;
	}
}
inline void swap(int &x,int &y){x^=y,y^=x,x^=y;}
inline long long lca(int x,int y){
	long long anss=0;
	if(d[x]<d[y]) swap(x,y);
	while(d[x]>d[y]) anss+=sum[x][lg[d[x]-d[y]]-1],x=fa[x][lg[d[x]-d[y]]-1];
	if(x==y)return anss;
	for(int k=lg[d[x]];k>=0;k--) if(fa[x][k]!=fa[y][k]) anss+=sum[x][k]+sum[y][k],x=fa[x][k],y=fa[y][k];
	//printf("%d %d %d\n",x,y,anss);
	if(ans[x]==ans[y]&&ans[x]) return anss+min(abs(f1[x]-f1[y]),min(f1[x]+f2[y],f2[x]+f1[y]));
	else return anss+sum[x][0]+sum[y][0];
}
int main(){
//	freopen("1.in","r",stdin);
//	freopen("1.out","w",stdout);
	memset(s.h,-1,sizeof(s.h));
	memset(c.h,-1,sizeof(c.h));
	register int i;
	scanf("%d%d%d",&n,&m,&k);
	for(i=1;i<=n;i++)lg[i]=lg[i-1]+(1<<lg[i-1]==i);
	for(i=1;i<=m;i++) scanf("%d%d%d",&xs,&y,&z),s.add(xs,y,z),s.add(y,xs,z);
	dfs(1,0);
	//for(i=1;i<=n;i++) printf("%d %d\n",f1[i],f2[i]);
	dfss(1,0);
	for(i=1;i<=k;i++) scanf("%d%d",&xs,&y),printf("%lld\n",lca(xs,y));
}
posted @ 2020-08-24 21:57  275307894a  阅读(47)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end