statement - 基环树 - 虚树 - 倍增

8 篇文章 0 订阅
7 篇文章 0 订阅

这个题写的真爽翻了。
题目大意就是给你一个树和基环树森林(内向),边有权,每次询问给你两个点集,问所有黑点到白点的路径中,边权最大值最小是多少。
题解:首先考虑树,直接建出虚树来跑一遍dfs即可。然后每个环会选出一些点来,那些点先计算出一个贡献,你只要把环倍长然后那些点排个序,在上面做倍增即可。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<climits>
#define gc getchar()
#define N 200010
#define LOG 22
#define INF (INT_MAX/100)
#define pb push_back
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
int zx[N],in[N],up[N][LOG],mxv[N][LOG],rup[N][LOG],rmx[N][LOG],d[N],ccnt,dfc,vis[N];
int val[N],xs[N],gx[N],s[N],stcl[N],ct[N],csz[N],p[N],wp[N],lst[N],bc[N],Log[N];
vector<int> g[N],cyc[N],a[N];struct edges{int to,pre,wgt;}e[N];int h[N],etop;
inline int add_edge(int u,int v,int w) { return e[++etop].to=v,e[etop].pre=h[u],e[etop].wgt=w,h[u]=etop; }
inline int inn()
{
	int x,ch;while((ch=gc)<'0'||ch>'9');
	x=ch^'0';while((ch=gc)>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch^'0');return x;
}
inline int incmp(int x,int y) { return in[x]<in[y]; }
inline int ctcmp(int x,int y) { return ct[x]<ct[y]; }
int dfs(int x,int z)
{
	zx[x]=z,in[x]=++dfc;
	for(int i=1;i<=Log[d[x]];i++)
		up[x][i]=up[up[x][i-1]][i-1],
		mxv[x][i]=max(mxv[x][i-1],mxv[up[x][i-1]][i-1]);
	for(int i=h[x],y;i;i=e[i].pre)
		d[y=e[i].to]=d[x]+1,up[y][0]=x,mxv[y][0]=e[i].wgt,dfs(y,z);
	return 0;
}
inline int getLCA(int x,int y)
{
	if(d[x]<d[y]) swap(x,y);
	for(int i=Log[d[x]];i>=0;i--)
		if(d[up[x][i]]>=d[y]) x=up[x][i];
	if(x==y) return x;
	for(int i=Log[d[x]];i>=0;i--)
		if(up[x][i]^up[y][i]) x=up[x][i],y=up[y][i];
	return up[x][0];
}
inline int getmx(int x,int y)
{
	if(d[x]<d[y]) swap(x,y);int ans=0;
	for(int i=Log[d[x]];i>=0;i--)
		if(d[up[x][i]]>=d[y]) ans=max(ans,mxv[x][i]),x=up[x][i];
	if(x==y) return ans;
	for(int i=Log[d[x]];i>=0;i--) if(up[x][i]^up[y][i])
		ans=max(ans,max(mxv[x][i],mxv[y][i])),x=up[x][i],y=up[y][i];
	return max(ans,max(mxv[x][0],mxv[y][0]));
}
int dfs_virtual(int x,int rt)
{
	if(val[x]==1) xs[x]=x;int ans=INF;
	if(val[x]==-1)
	{
		if(xs[x]) ans=min(ans,getmx(x,xs[x]));
		else if(x==rt) gx[rt]=0;
		else gx[rt]=min(gx[rt],getmx(x,rt));
	}
	for(int i=0,y;i<(int)g[x].size();i++)
		xs[y=g[x][i]]=xs[x],ans=min(ans,dfs_virtual(y,rt));
	return ans;
}
inline int getans(vector<int> &a,int rt)
{
	if(!a.size()) return 0;
	sort(a.begin(),a.end(),incmp);
	int t=1,sl=1;s[1]=stcl[1]=rt;
	for(int i=0+(a[0]==rt);i<(int)a.size();i++)
	{
		int x=a[i],y=s[t],z=s[t-1],c;
		while(d[c=getLCA(x,y)]<=d[z]) g[z].pb(y),stcl[++sl]=y,y=z,t--,z=s[t-1];
		if(c^y) g[c].pb(y),stcl[++sl]=y,s[t]=c;s[++t]=x;
	}
	while(t>1) g[s[t-1]].pb(s[t]),stcl[++sl]=s[t],t--;
	xs[rt]=0;int ans=dfs_virtual(rt,rt);
	for(int i=1;i<=sl;i++) g[stcl[i]].clear();
	return ans;
}
inline int query(int x,int y)
{
	int ans=0,k;
	if(ct[x]<ct[y]) k=ct[y]-ct[x];
	else k=csz[bc[x]]-ct[x]+ct[y];
	for(int i=0;k;i++,k>>=1) if(k&1)
		ans=max(ans,rmx[x][i]),x=rup[x][i];
	return ans;
}
inline int solve(vector<int> c)
{
	sort(c.begin(),c.end(),ctcmp);
	int n=(int)c.size(),tot=0;
	for(int i=0;i<n;i++)
		if(gx[c[i]]<INF) tot|=1;else tot|=2;
	if(tot<3) return INF;
	for(int i=0;i<n-1;i++) c.pb(c[i]);	
	n=(int)c.size();int ans=INF;
	for(int i=n-1,nxt=-1;i>=0;i--)
	{
		if(nxt>=0&&gx[c[i]]<INF)
			ans=min(ans,max(gx[c[i]],query(c[i],c[nxt])));
		if(val[c[i]]==1) nxt=i;
	}
	return ans;
}
int main()
{
	int n=inn(),m=inn();
	for(int i=2;i<=n;i++) Log[i]=Log[i>>1]+1;
	for(int i=1,x,y;i<=m;i++) x=inn(),y=inn(),p[y]=x,wp[y]=i;
	for(int i=1;i<=n;i++) if(!p[i]) p[i]=i,wp[i]=INF;
	for(int i=1,x,tm;i<=n;i++)
	{
		for(x=i;!vis[x];vis[x]=i,x=p[x]);if(vis[x]^i) continue;
		for(ccnt++,tm=0;!ct[x];ct[x]=++tm,bc[x]=ccnt,x=p[x],csz[ccnt]++);
	}
	for(int i=1;i<=n;i++) if(ct[i]) rup[i][0]=p[i],rmx[i][0]=wp[i];
	for(int i=1;i<LOG;i++)
		for(int x=1;x<=n;x++) if(ct[x])
			rup[x][i]=rup[rup[x][i-1]][i-1],
			rmx[x][i]=max(rmx[x][i-1],rmx[rup[x][i-1]][i-1]);
	for(int i=1;i<=n;i++) if(!ct[i]) add_edge(p[i],i,wp[i]);
	for(int i=1;i<=n;i++) if(ct[i]) dfc=0,d[i]=1,dfs(i,i);
	/*for(int i=1;i<=n;i++) if(ct[i])
	{
		debug(i)sp,debug(bc[i])sp,debug(ct[i])sp,debug(csz[bc[i]])ln;
		for(int j=0;j<=Log[csz[bc[i]]];j++) debug(j)sp,debug(rup[i][j])sp,debug(rmx[i][j])ln;
		cerr ln;
	}
	for(int i=1;i<=n;i++) if(!ct[i])
	{
		debug(i)sp,debug(d[i])sp,debug(zx[i])sp,debug(in[i])ln;
		for(int j=0;j<=Log[d[i]];j++) debug(j)sp,debug(up[i][j])sp,debug(mxv[i][j])ln;
		cerr ln;
	}*/
	for(int q=inn();q;q--)
	{
		int tc=inn();for(int i=1;i<=tc;i++) val[lst[i]=inn()]=1;
		int fc=inn();for(int i=tc+1;i<=tc+fc;i++) val[lst[i]=inn()]=-1;
		for(int i=1;i<=tc+fc;i++) a[zx[lst[i]]].pb(lst[i]);int ans=INF;
		for(int i=1,x;i<=tc+fc;i++) if(a[x=zx[lst[i]]].size())
		{
			gx[x]=INF,ans=min(ans,getans(a[x],x)),a[x].clear();
			if(gx[x]<INF||val[x]==1) cyc[bc[x]].pb(x);
		}
		for(int i=1,x;i<=tc+fc;i++)
			if(cyc[x=bc[zx[lst[i]]]].size())
				ans=min(ans,solve(cyc[x])),cyc[x].clear();
		for(int i=1;i<=tc+fc;i++) val[lst[i]]=0;
		if(ans<=m) printf("%d\n",ans);
		else printf("OK\n");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值