【NOI P模拟赛】仙人掌(圆方树,树形DP)
题面

n
n
n 个点,
m
m
m 条边。
1
≤
n
≤
1
0
5
,
n
−
1
≤
m
≤
2
×
1
0
5
1\leq n\leq 10^5,n-1\leq m\leq 2\times10^5
1≤n≤105,n−1≤m≤2×105 。
题解
直接求行列式是不现实的,我们可以通过行列式的定义式来思考解法。
一个会产生贡献的排列,相当于要每个点选一个邻接点当爹,每个点的爹必须不一样。
如果这个点度为 1,那么它和它的邻接点必须配对,然后相当于删掉了。同时这两个点使得该排列的贡献乘上 -1 。类推下去,很容易就能解决一棵树的情况,或者剪掉该图所有枝杈。
然后对于一个干干净净的环,则只有两个大方向:要么相邻的两两配对,要么朝向同一个方向认爹。相邻的配对,总点数只能是偶数,总共两种方案,每个方案的每一对都要使排列贡献乘上 -1,所以总共会使排列的贡献乘上
2
×
(
−
1
)
总
点
数
/
2
2\times(-1)^{总点数/2}
2×(−1)总点数/2 。朝同一个方向认爹,也有两种方案,顺时针和逆时针,每种方案的贡献都是
(
−
1
)
总
点
数
−
1
(-1)^{总点数-1}
(−1)总点数−1 (你想想就知道了),两种方案总共会使排列的贡献乘上
2
×
(
−
1
)
总
点
数
−
1
2\times(-1)^{总点数-1}
2×(−1)总点数−1 。
把这两种模式合并起来怎么办呢?
你可以暴力拓扑删点拆环统计贡献……
也可以用比较清晰的DP做法,在仙人掌上 DP ,建一个圆方树做树形DP。
不过具体的 DP 流程还是根暴力删点拆环差不多
时间复杂度
O
(
n
)
O(n)
O(n) 。
CODE
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<random>
#include<vector>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 200005
#define LL long long
#define ULL unsigned long long
#define DB double
#define lowbit(x) (-(x) & (x))
#define ENDL putchar('\n')
#define FI first
#define SE second
LL read() {
LL f=1,x=0;int s = getchar();
while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
while(s >= '0' && s <= '9') {x = (x<<3) + (x<<1) + (s^48); s = getchar();}
return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar('0'+(x%10));}
void putnum(LL x) {
if(!x) {putchar('0');return ;}
if(x<0) {putchar('-');x = -x;}
return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}
const int MOD = 993244853;
int n,m,s,o,k;
int ind[MAXN];
int hd[MAXN],v[MAXN<<1],nx[MAXN<<1],cne;
void ins(int x,int y) {
ind[y] ++;
nx[++ cne] = hd[x]; v[cne] = y; hd[x] = cne;
}
vector<int> g[MAXN];
bool f[MAXN];
int fa[MAXN];
int d[MAXN],id[MAXN];
void dfs0(int x,int ff) {
d[x] = d[fa[x] = ff] + 1;
for(int i = hd[x];i;i = nx[i]) {
int y = v[i];
if(y != ff) {
if(!d[y]) {
dfs0(y,x);
}
else if(d[y] < d[x]) {
int p = x;
f[++ n] = 1;
while(p != y) {
id[p] = n;
p = fa[p];
}
g[n].push_back(y);
g[y].push_back(n);
}
}
}
if(id[x]) {
g[id[x]].push_back(x);
g[x].push_back(id[x]);
}
else {
g[ff].push_back(x);
g[x].push_back(ff);
}
return ;
}
int dp[MAXN][2];
bool df[MAXN];
int tmp[MAXN],tp,tmd[MAXN];
void dfs(int x,int ff) {
if(f[x]) {
int dpp = 1;
for(int i = 0;i < (int)g[x].size();i ++) {
int y = g[x][i];
if(y != ff) {
dfs(y,x);
dpp = dpp *1ll* dp[y][1] % MOD;
}
}
tp = 0;
int ad = 0;
for(int i = 0;i < (int)g[x].size();i ++) {
int y = g[x][i];
tmp[tp ++] = y;
if(y == ff) ad = tp-1;
}
int D[2][2] = {};
D[0][0] = 1;
D[1][1] = 1;
for(int i = (ad+1)%tp;i != ad;i = (i+1)%tp) {
int y = tmp[i];
for(int j = 0;j < 2;j ++) {
int A = (MOD- D[j][1]*1ll*dp[y][1] % MOD + D[j][0]*1ll*dp[y][0] % MOD) % MOD;
int B = D[j][0]*1ll*dp[y][1] % MOD;
D[j][0] = A;
D[j][1] = B;
}
}
int A = 0,B = 0;
(A += D[1][0]) %= MOD;
(B += D[0][0]) %= MOD;
(A += MOD-D[0][1]) %= MOD;
dp[x][0] = A;
dp[x][1] = B;
if(tp & 1) (dp[x][0] += dpp*2ll % MOD) %= MOD;
else (dp[x][0] += MOD-dpp*2ll%MOD) %= MOD;
}
else {
dp[x][1] = 1;
for(int i = 0;i < (int)g[x].size();i ++) {
int y = g[x][i];
if(y != ff) {
dfs(y,x);
if(!f[y]) {
int A = (MOD-dp[x][1] *1ll* dp[y][1] % MOD + dp[x][0]*1ll*dp[y][0]%MOD)%MOD;
int B = dp[x][1] *1ll* dp[y][0] % MOD;
dp[x][0] = A; dp[x][1] = B;
}
else {
int A = (dp[x][1] *1ll* dp[y][0] % MOD + dp[x][0] *1ll* dp[y][1]%MOD) % MOD;
int B = dp[x][1] *1ll* dp[y][1] % MOD;
dp[x][0] = A; dp[x][1] = B;
}
}
}
}
return ;
}
int main() {
freopen("cactus.in","r",stdin);
freopen("cactus.out","w",stdout);
n = read();
m = read();
for(int i = 1;i <= m;i ++) {
s = read();o = read();
ins(s,o);ins(o,s);
}
dfs0(1,0);
dfs(1,0);
AIput(dp[1][0],'\n');
return 0;
}
【NOI P模拟赛】仙人掌(圆方树,树形DP)的更多相关文章
- 2019.03.29 bzoj5463: [APIO2018] 铁人两项(圆方树+树形dp)
传送门 题意简述:给你一张无向图,问你满足存在从a−>b−>ca->b->ca−>b−>c且不经过重复节点的路径的有序点对(a,b,c)(a,b,c)(a,b,c) ...
- [APIO2018]铁人两项——圆方树+树形DP
题目链接: [APIO2018]铁人两项 对于点双连通分量有一个性质:在同一个点双里的三个点$a,b,c$,一定存在一条从$a$到$c$的路径经过$b$且经过的点只被经过一次. 那么我们建出原图的圆方 ...
- loj2587 「APIO2018」铁人两项[圆方树+树形DP]
主要卡在一个结论上..关于点双有一个常用结论,也经常作为在圆方树/简单路径上的良好性质,对于任意点双内互不相同的三点$s,c,t$,都存在简单路径$s\to c\to t$,证明不会.可以参见clz博 ...
- 【NOI P模拟赛】最短路(树形DP,树的直径)
题面 给定一棵 n n n 个结点的无根树,每条边的边权均为 1 1 1 . 树上标记有 m m m 个互不相同的关键点,小 A \tt A A 会在这 m m m 个点中等概率随机地选择 k k k ...
- 校内模拟赛 : Rima —— 字典树+树形DP
首先说一下,对一个刚学Trie树的蒟蒻来说(就是我),这道题是一道好题.Trie树比较简单,所以就不详细写了. Rima 内存限制:256 MiB 时间限制:1000 ms 标准输入输出 题目类型:传 ...
- 仙人掌&圆方树学习笔记
仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...
- 仙人掌 && 圆方树 && 虚树 总结
仙人掌 && 圆方树 && 虚树 总结 Part1 仙人掌 定义 仙人掌是满足以下两个限制的图: 图完全联通. 不存在一条边处在两个环中. 其中第二个限制让仙人掌的题做 ...
- 仙人掌&圆方树
仙人掌&圆方树 Tags:图论 [x] [luogu4320]道路相遇 https://www.luogu.org/problemnew/show/P4320 [ ] [SDOI2018]战略 ...
- [APIO2018] Duathlon 铁人两项 圆方树,DP
[APIO2018] Duathlon 铁人两项 LG传送门 圆方树+简单DP. 不会圆方树的话可以看看我的另一篇文章. 考虑暴力怎么写,枚举两个点,答案加上两个点之间的点的个数. 看到题面中的一句话 ...
- UOJ.87.mx的仙人掌(圆方树 虚树)(未AC)
题目链接 本代码10分(感觉速度还行..). 建圆方树,预处理一些东西.对询问建虚树. 对于虚树上的圆点直接做:对于方点特判,枚举其所有儿子,如果子节点不在该方点代表的环中,跳到那个点并更新其val, ...
随机推荐
- 【物联网串口服务器通信经验教程】Modbus网关协议转换
在前面的文章中,我们已经详细地介绍了Modbus网关的几种主要类型,今天,就让我们来介绍一下其中简单协议转换的处理过程. 简单协议转换是最常规.最普遍的Modbus网关功能,也是数据处理效率最高Mod ...
- 论文解读(USIB)《Towards Explanation for Unsupervised Graph-Level Representation Learning》
论文信息 论文标题:Towards Explanation for Unsupervised Graph-Level Representation Learning论文作者:Qinghua Zheng ...
- Nodejs实现图片的上传、压缩预览、定时删除
前言 我们程序员日常都会用到图片压缩,面对这么常用的功能,肯定要尝试实现一番.第一步,node基本配置 这里我们用到的是koa框架,它可是继express框架之后又一个更富有表现力.更健壮的web框架 ...
- js中通过ajax调用网上接口
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8&qu ...
- 到点了开始网抑云(悲)但是用python(整活)
写在前面的一点网抑云: 爱情不是随便许诺好了不想再说了没错 是我那么多的冷漠 让你感觉到无比的寂寞不过 一个女人的不仅仅渴望得到的一个承诺我害怕欺骗也害怕寂寞更害怕我的心会渐渐地凋落爱情不是随便许诺好 ...
- Windows下maven配置环境变量
右键 "计算机",选择 "属性",之后点击 "高级系统设置",点击"环境变量",来设置环境变量,有以下系统变量需要配置: ...
- Python之枚举法解数学题
作为初二的学生,数学题总是令我苦恼的问题.尤其是我们这里的预备班考试(即我们这里最好的两所高中提前一年招生,选拔尖子生的考试)将近,我所面对的数学题越发令人头疼. 这不,麻烦来了: 如图,在正方形AB ...
- Android 12(S) 图像显示系统 - drm_hwcomposer 简析(下)
必读: Android 12(S) 图像显示系统 - 开篇 合成方式 合成类型的定义:/hardware/interfaces/graphics/composer/2.1/IComposerClien ...
- 基于SqlSugar的开发框架循序渐进介绍(10)-- 利用axios组件的封装,实现对后端API数据的访问和基类的统一封装处理
在SqlSugar的开发框架的后端,我们基于Web API的封装了统一的返回结果,使得WebAPI的接口返回值更加简洁,而在前端,我们也需要统一对返回的结果进行解析,并获取和Web API接口对应的数 ...
- C语言-数据结构-结构体
一.结构体的定义 数组(Array)是一组具有相同类型的数据的集合.但在实际的编程过程中,我们往往还需要一组类型不同的数据,例如对于学生信息登记表,姓名为字符串,学号为整数,年龄为整数,所在的学习小组 ...