【BZOJ】5463: [APIO2018] 铁人两项-广义圆方树

传送门:bzoj5463


题解

建出广义圆方树。

将圆点权值看做-1,方点权值看做所连接的圆点数量。
两个圆点分别作为 s , f s,f s,f点时 c c c点的方案数即它们在圆方树上简单路径的权值和。

转而枚举每个点被计算次数(经过路径数),时间复杂度 O ( n ) O(n) O(n)


代码

#include
#define gc getchar
using namespace std;
const int N=2e5+10;
typedef long long ll;

int n,m,num,sz[N],val[N],S;
int df[N],low[N],stk[N],top,dfn;
ll ans;

char cp;
template<class T>inline void rd(T &x)
{
	cp=gc();x=0;int f=0;
	for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
	for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
	if(f) x=-x;
}

struct gra{
	int head[N],to[N<<1],nxt[N<<1],tot;
	inline void lk(int u,int v)
	{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
}A,B;

void tar(int x)
{
	int i,j,tp;sz[x]=1;val[x]=-1;
	df[x]=low[x]=++dfn;stk[++top]=x;
	for(i=A.head[x];i;i=A.nxt[i]){
	    j=A.to[i];
		if(!df[j]){
			tar(j);
			if(low[j]>=df[x]){
				B.lk(x,++num);val[num]=1;//方点权值 
			   	for(;;){
			   	    tp=stk[top--];sz[num]+=sz[tp];
			   	    val[num]++;B.lk(num,tp);
				    if(tp==j) break;	
				}
				sz[x]+=sz[num];
			}else low[x]=min(low[x],low[j]); 
		}else low[x]=min(low[x],df[j]);
    }
}
 
void dfs(int x)
{
    if(x<=n) ans-=(S-1);//圆点自身作为起点 
	ans+=(ll)(S-sz[x])*sz[x]*val[x];//
	for(int j,i=B.head[x];i;i=B.nxt[i]){
		j=B.to[i];
		ans+=(ll)(S-sz[j])*sz[j]*val[x];//每个子树结点作为起点 
		dfs(j);
	}
} 

int main(){
	int i,j,x,y;
    rd(n);rd(m);num=n;
    for(i=1;i<=m;++i){
    	rd(x);rd(y);
		A.lk(x,y);A.lk(y,x);
	}
	for(i=1;i<=n;++i) if(!df[i]) {tar(i);S=sz[i];dfs(i);}
	printf("%lld",ans);
	return 0;
}

你可能感兴趣的:(仙人掌,圆方树)