●洛谷P3687 [ZJOI2017]仙人掌
题链:
https://www.luogu.org/problemnew/show/P3687
题解:
计数DP,树形DP。
(首先对于这个图来说,如果初始就不是仙人掌,那么就直接输出0)
然后由于本来图中就存在于环中的边,不可能再次被包含,
所以图中的环就把这个图分为的若干颗树。
那么答案就是分别求出每颗树的方案数并相乘。
现在问题变为了求:把一颗树通过连边使得仍然是仙人掌的方案数。
定义如下3个数组:
f[u]:表示u这颗子树中没有一条从u到子树内某个的节点的路径可以向其它子树连边的方案数。
g[u]:表示u这颗子树中有一条从u到子树内某个节点的路径可以其它子树连边的方案数。
以及一个预处理的h[n]:
表示有n个元素,每个元素可以选择另一个元素与其两两匹配或者该元素不与任何元素匹配的方案数。
先看看h[n]的求法:
h[n]=h[n-1]+h[n-2]*(n-1)
即表明第n个元素要么独立,要么与另外(n-1)个元素中的某一个匹配。
然后看看怎样DP:
对于当前的子树的根u,v是其儿子节点,num是其儿子节点个数
$$f[u]=\sum g[v] \times h[num] $$
因为num个儿子有h[num]种匹配方案数,所以乘上h[num]
(所谓的匹配就是把g[v]中的那条路径的末端和与它匹配的那个v'的g[v']中的那条路径的末端连边)
然后还要求出当前的g[u]:
$$g[u]=f[u]+\sum g[v] \times h[num-1] \times num$$
含义如下:
由于u点自己可以成为g[u]中的那条路径的末端,所以$$+f[u]$$
然后u还可以选择一个儿子g[son]中的路径来连上自己形成新的g[u]中的路径。
这样的话,方案数是 $g[son]* \sum_{v!=son} g[v] \times h[num-1] = \sum g[v] \times h[num-1]$
又因为u有num个儿子,即有num中选法,所以:
$$+\sum g[v] \times h[num-1] \times num $$
代码:
#include<bits/stdc++.h>
#define MAXN 500005
#define MOD 998244353
#define rint register int
using namespace std;
int Case,N,M,dnt,cactus,ANS;
int dfn[MAXN],lu[MAXN],fa[MAXN],f[MAXN],g[MAXN],h[MAXN];
struct node{
int id,odr;
bool operator < (const node &rtm) const{
return odr<rtm.odr;
}
}A[MAXN];
struct Edge{
int ent;
int to[MAXN*4],nxt[MAXN*4],head[MAXN];
void Reset(int n){
for(rint i=1;i<=n;i++) head[i]=0; ent=2;
}
void Adde(int u,int v){
to[ent]=v; nxt[ent]=head[u]; head[u]=ent++;
}
}E;
void dfs(int u,int dad){
dfn[u]=++dnt; fa[u]=dad;
for(int e=E.head[u];e;e=E.nxt[e]){
int v=E.to[e];
if(dfn[v]) continue;
dfs(v,u);
}
}
void DP(int u,int rt){
lu[u]=-1; f[u]=1; int num=0,gson=1;
for(int e=E.head[u];e;e=E.nxt[e]){
int v=E.to[e];
if(lu[v]!=1||v==fa[u]) continue;
DP(v,0); num++;
gson=1ll*gson*g[v]%MOD;
}
f[u]=1ll*gson*h[num]%MOD;
g[u]=(1ll*f[u]+1ll*gson*h[num-1]%MOD*num)%MOD;
}
int main(){
h[0]=h[1]=1;
for(int i=1;i<=500000;i++) h[i]=(1ll*h[i-1]+1ll*h[i-2]*(i-1))%MOD;
for(scanf("%d",&Case);Case;Case--){
scanf("%d%d",&N,&M);
E.Reset(N); dnt=0; cactus=1; ANS=1;
for(rint i=1;i<=N;i++) lu[i]=dfn[i]=fa[i]=0;
for(int i=1,a,b;i<=M;i++)
scanf("%d%d",&a,&b),E.Adde(a,b),E.Adde(b,a);
dfs(1,0);
for(int e=1,u,v;e<=M;e++){
u=E.to[e*2]; v=E.to[e*2+1];
if(dfn[u]>dfn[v]) swap(u,v);
while(v!=u){
lu[v]++;
if(lu[v]>2){cactus=0;break;}
v=fa[v];
}
}
if(!cactus){printf("%d\n",0); continue;}
for(int i=1;i<=N;i++) A[i]=(node){i,dfn[i]};
sort(A+1,A+N+1);
for(int i=1,u;i<=N;i++){
u=A[i].id;
if(lu[u]==-1) continue;
DP(u,1); ANS=1ll*ANS*f[u]%MOD;
}
printf("%d\n",ANS);
}
return 0;
}
●洛谷P3687 [ZJOI2017]仙人掌的更多相关文章
- 洛谷 P4244 [SHOI2008]仙人掌图 II 解题报告
P4244 [SHOI2008]仙人掌图 II 题目背景 题目这个II是和SHOI2006的仙人掌图区分的,bzoj没有. 但是实际上还是和bzoj1023是一个题目的. 题目描述 如果某个无向连通图 ...
- 洛谷P4244 [SHOI2008]仙人掌图 II
传送门 首先不考虑带环的仙人掌,如果只是一棵普通的树,可以通过dp求每棵子树中的最长链和次长链求树的直径. 那么如果dfs的时候遇到了环,应该用环上的两点挂着的最长链加上两点间的距离来更新树的直径,并 ...
- ●洛谷P3688 [ZJOI2017]树状数组
题链: https://www.luogu.org/problemnew/show/P3688题解: 二维线段树. 先不看询问时l=1的特殊情况. 对于一个询问(l,r),如果要让错误的程序得到正确答 ...
- 2018.10.29 洛谷P4129 [SHOI2006]仙人掌(仙人掌+高精度)
传送门 显然求出每一个环的大小. Ans=∏i(siz[i]+1)Ans=\prod_i(siz[i]+1)Ans=∏i(siz[i]+1) 注意用高精度存答案. 代码: #include<b ...
- 洛谷P5211 [ZJOI2017]字符串(线段树+乱搞)
题面 传送门 题解 为什么大佬们全都是乱搞的--莫非这就是传说中的暴力能进队,乱搞能AC-- 似乎有位大佬能有纯暴力+玄学优化\(AC\)(不算上\(uoj\)的\(Hack\)数据的话--这要是放到 ...
- 洛谷 P3688 - [ZJOI2017]树状数组(二维线段树+标记永久化)
题面传送门 首先学过树状数组的应该都知道,将树状数组方向写反等价于前缀和 \(\to\) 后缀和,因此题目中伪代码的区间求和实质上是 \(sum[l-1...n]-sum[r...n]=sum[l-1 ...
- 洛谷P3688/uoj#291. [ZJOI2017]树状数组
传送门(uoj) 传送门(洛谷) 这里是题解以及我的卡常数历程 话说后面那几组数据莫不是lxl出的这么毒 首先不难发现这个东西把查询前缀和变成了查询后缀和,结果就是查了\([l-1,r-1]\)的区间 ...
- 【BZOJ4784】[ZJOI2017]仙人掌(Tarjan,动态规划)
[BZOJ4784][ZJOI2017]仙人掌(Tarjan,动态规划) 题面 BZOJ 洛谷 题解 显然如果原图不是仙人掌就无解. 如果原图是仙人掌,显然就是把环上的边给去掉,变成若干森林连边成为仙 ...
- [洛谷日报第62期]Splay简易教程 (转载)
本文发布于洛谷日报,特约作者:tiger0132 原地址 分割线下为copy的内容 [洛谷日报第62期]Splay简易教程 洛谷科技 18-10-0223:31 简介 二叉排序树(Binary Sor ...
随机推荐
- 201621123057 《Java程序设计》第9周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 在上一周的总结上做了一点补充 1.2 选做:收集你认为有用的代码片段 2. 书面作业 本次作业题集集合 1. ...
- 20145237 《Java程序设计》第八周学习总结
20145237 <Java程序设计>第八周学习总结 教材学习内容总结 第十五章 通用API 15.1 日志 日志API简介 • java.util.logging包提供了日志功能相关类与 ...
- sql 几种循环方式
1:游标方式 ALTER PROCEDURE [dbo].[testpro] as ) --日期拼接 ) --仪表编号 ) --数据采集表 ) --数据采集备份表 ) ) begin set @yea ...
- leetcode题解 6.ZigZag Conversion
6.ZigZag Conversion 题目: The string "PAYPALISHIRING" is written in a zigzag pattern on a gi ...
- Spring Security 入门(3-11)Spring Security 的登录密码验证过程 UsernamePasswordAuthenticationFilter
认证过程如下 一.先判断请求(请求必须是post请求)地址是否为配置的 login-processing-url 值(默认/j_spring_security_check),如果不是,则放行,进入下一 ...
- Lua编写wireshark插件初探——解析Websocket上的MQTT协议
一.背景 最近在做物联网流量分析时发现, App在使用MQTT协议时往往通过SSL+WebSocket+MQTT这种方式与服务器通信,在使用SSL中间人截获数据后,Wireshark不能自动解析出MQ ...
- 理解JavaScript中函数方法
1.函数声明和函数表达式 通过字面量创建函数的方式有两种函数声明和函数表达式: 函数声明: function sum(x, y) { var result = x + y; return result ...
- Linux下安装Python3.x和第三方库
如果本机安装了python2,尽量不要管他,使用python3运行python脚本就好,因为可能有程序依赖目前的python2环境, 比如yum!!!!! 不要动现有的python2环境! 不要动现有 ...
- Codeforces Round #426 (Div. 2)
http://codeforces.com/contest/834 A. The Useless Toy 题意: <,>,^,v这4个箭头符号,每一个都可以通过其他及其本身逆时针或者顺时针 ...
- UVAlive-2554 Snakes & Ladders---BFS状态的存储
题目链接: https://vjudge.net/problem/UVALive-2554 题目大意: 题目的大概意思是又N*N的棋盘,编号从1 到 N*N 棋盘中分布着蛇和梯子玩家在位置1处, ...