义乌集训Day 4 T3

23 篇文章 0 订阅

题目链接

通过题目可以看到,每个点 的度数都是小于3的,而且每个点只可能最多属于一个简单环,所以我们可以考虑用dfs 找到每一个环然后将每个环缩减成为一个点再将其和原来的边进行重新连边

然后我们就可以发现,建完新图之后的题目就简化成了一棵树

然后又可以发现一条性质:
两个简单环之间的路径数目就是就是 两个简单环之间个数的2k,因为每一个路径都可以分为上下两种走法

之后就进行树形DP
f [ x ] 表 示 以 x 点 位 节 点 的 半 个 眼 镜 有 多 少 个 f[x]表示以x点位节点的半个眼镜有多少个 f[x]x
当这个第是一个简单环的时候,就可以从这个点走到儿子结点,可以构成两倍的半个眼镜 f [ x ] = f [ x ] + f [ y ] ∗ 2 ; f[x]=f[x]+f[y]*2; f[x]=f[x]+f[y]2;

当这个第是一个普通点的时候,就只有儿子走到自己 f [ x ] = f [ x ] + f [ y ] ; f[x]=f[x]+f[y]; f[x]=f[x]+f[y];

a n s [ x ] 表 示 x 为 节 点 的 手 铐 数 , y 是 子 节 点 ans[x]表示x为节点的手铐数,y是子节点 ans[x]x,y
计算答案就是将半个眼镜 乘在一起构成整个眼镜 a n s [ x ] = a n s [ x ] + f [ x ] ∗ f [ y ] + a n s [ y ] ; ans[x]=ans[x]+f[x]*f[y]+ans[y]; ans[x]=ans[x]+f[x]f[y]+ans[y];

#include<bits/stdc++.h>
#define maxn 2000010
#define mod 19260817
#define int long long
using namespace std;
inline int read()
{
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch)){res=(res<<1)+(res<<3)+(ch&15);ch=getchar();}
	return res*f;
}
int n,m;
int vis[maxn];
int num[maxn],numal;
int pr[maxn];
int dp[maxn];
int zs[maxn];
int ans[maxn];
int tot,head[maxn];
struct node{
	int to,ne;
}edge[maxn*2];
inline void add(int x,int y)
{
	edge[++tot].to=y;
	edge[tot].ne=head[x];
	head[x]=tot;
}
inline void dfs(int x,int fa)
{
	
	if(vis[x]==2)return;
	if(vis[x]==1)
	{
		int k=pr[x];
		num[++numal]=1;
		zs[x]=numal;
		vis[x]=2;
		while(k!=x)
		{
			num[numal]++;
			zs[k]=numal;
			vis[k]=2;
			k=pr[k];
		}
		return;
	}
	vis[x]=1;
    for(int i=head[x];i;i=edge[i].ne)
    {
    	int to=edge[i].to;
    	if(to==fa)continue;
        pr[to]=x;
        dfs(to,x);
	}
}
int u[maxn],v[maxn];
inline void Dp(int x,int fa)
{
	ans[x]=0;
     if(num[x]>1)dp[x]=1;
	 else dp[x]=0;
	 
	 for(int i=head[x];i;i=edge[i].ne)
	 {
	 	int to=edge[i].to;
	 	if(to==fa)continue;
	 	Dp(to,x);
	 	ans[x]=(ans[x]+(dp[x]*dp[to])%mod+ans[to])%mod;
	 	if(num[x]>1)dp[x]=(dp[x]+dp[to]*2)%mod;
	 	else dp[x]=(dp[x]+dp[to])%mod;
     }	
}
signed main()
{
	n=read();m=read();
	for(int i=1;i<=m;i++)
	{
	   u[i]=read();v[i]=read();
	   add(u[i],v[i]);add(v[i],u[i]);
	}
	dfs(1,1);
	for(int i=1;i<=n;i++)if(vis[i]!=2)num[++numal]=1,zs[i]=numal;

	tot=0;memset(head,0,sizeof(head));
	for(int i=1;i<=m;i++)
	{
		if(zs[u[i]]!=zs[v[i]])
		{
		add(zs[u[i]],zs[v[i]]);
		add(zs[v[i]],zs[u[i]]);	
		}
	}
	Dp(1,1);
	printf("%d\n",ans[1]);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值