codeforces231E Cactus

https://codeforces.com/contest/231/problem/E

看错题了。。。看成了要求每个点只经过一次的路径,然后想了一年。。。觉得这题比前面的2900分题难多了然后开始思考人生

要求每条路径只经过一次,那就很简单了

由于在仙人掌中每一个点最多属于一个环,那么在dfs树的过程中可以直接求出每个环的根节点(深度最小的)在哪,就相当于缩点了,还可以把缩点后的每个点的父亲节点是谁很方便的求出来

然后答案就是u,v的路径上所有的环的个数的2次幂了,包括u,v本身所在的环,这个用lca搞搞就好了,因为路径是边不重复,那么就不用考虑连接缩点以后图的边他原来的两个点是谁,如果是每个点只经过一次的路径就要考虑。。。现在还不会。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxl=3e5+10;
const int mod=1e9+7;

int n,m,ans,cas,k,cnt,tot;
int a[maxl],b[maxl],cid[maxl],val[maxl];
int dy[maxl],dep[maxl],dis[maxl];
int f[19][maxl];
char s[maxl];
bool in[maxl],vis[maxl]; 
vector<int> e[maxl];
ll num[maxl];

inline void predfs(int u,int fa)
{
	vis[u]=true;cid[u]=u;
	for(int v:e[u])
	{
		if(v==fa) continue;
		if(vis[v])
		{
			if(dep[v]<dep[u])
				cid[u]=v,val[v]=1;
			continue;
		}
		dep[v]=dep[u]+1;
		predfs(v,u);
		if(cid[v]!=v && cid[v]>0)
			cid[u]=cid[v];
	}
	for(int v:e[u])
	{
		if(v==fa) continue;
		if(cid[v]==v && dep[v]>dep[u])
			f[0][v]=cid[u];
	}
}

inline void dfs(int u)
{
	vis[u]=true;
	for(int v:e[u])
	{
		if(vis[v]) continue;
		if(cid[v]==v)
		{
			dis[v]=dis[cid[u]]+val[v];
			dep[v]=dep[cid[u]]+1;
		}
		dfs(v);
	}
}

inline void prework()
{
	scanf("%d%d",&n,&m);
	num[0]=1;
	for(int i=1;i<=n;i++)
		num[i]=num[i-1]*2%mod;
	int u,v;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		e[u].push_back(v);
		e[v].push_back(u); 
	}
	predfs(1,0);
	for(int i=1;i<=n;i++) vis[i]=false;
	dfs(1);
} 

inline int lca(int u,int v)
{
	if(dep[u]<dep[v])
		swap(u,v);
	for(int i=18;i>=0;i--)
	if(((dep[u]-dep[v])>>i)&1)
		u=f[i][u];
	if(u==v) return u;
	for(int i=18;i>=0;i--)
	if(f[i][u]!=f[i][v])
		u=f[i][u],v=f[i][v];
	return f[0][u];
}

inline void mainwork()
{
	for(int i=1;i<=18;i++)
		for(int j=1;j<=n;j++)
			f[i][j]=f[i-1][f[i-1][j]];
	scanf("%d",&k);int x,y,d,t;
	for(int i=1;i<=k;i++)
	{
		scanf("%d%d",&x,&y);
		d=lca(cid[x],cid[y]);
		t=dis[cid[x]]+dis[cid[y]]-2*dis[d];
		t+=val[d];
		printf("%lld\n",num[t]);
	}
}

inline void print()
{
	
}

int main()
{
	int t=1;
	//scanf("%d",&t);
	for(cas=1;cas<=t;cas++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值