Hdu第八场 树形dp+基环树
每个牌背面的数字朝正面的数字连一条有向边
则题目变为问你最少翻转多少次 能使得每个数字的入度不超过1
首先判断图中每个连通块是不是树或者基环树 因为只有树或者基环树能使得每个点的入度不超过1
判的话就直接判断边的数量与点的数量之间的大小关系如果边数<=点数则可行
对于树 我们进行两次dp 第一次dp出以一个点为根需要翻转的次数 第二次就可以转移出以每个点为根需要翻转的次数
对于基环树 我们先看环里面需要翻转的次数 再判断环之外需要翻转的次数 环之外的方向是确定的 很好判断
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int, int> PII;
const ll mod = ;
ll powmod(ll a, ll b)
{
ll res = ;
a %= mod;
assert(b >= );
for (; b; b >>= )
{
if (b & )
{
res = res * a % mod;
}
a = a * a % mod;
}
return res;
}
ll gcd(ll a, ll b)
{
return b ? gcd(b, a % b) : a;
}
// head const int N = ;
int n, m, u, v, T, mt[N], hs[N], _;
vector<PII> e[N];
VI vec[N];
int vis[N], f[N], se[N], q[N], dp[N], pre[N], deg[N], fa[N];
PII chke[N];
int find(int u)
{
return f[u] == u ? u : f[u] = find(f[u]);
}
void solve()
{
n = ;
T++;
rep(i, , m + )
{
scanf("%d%d", &u, &v);
if (mt[u] != T) //离散化
{
mt[u] = T, hs[u] = n++;
}
if (mt[v] != T)
{
mt[v] = T, hs[v] = n++;
}
u = hs[u];
v = hs[v];
chke[i] = mp(u, v); //记录每条边
}
rep(i, , n) //初始化
e[i].clear(), vis[i] = , f[i] = i, vec[i].clear(), se[i] = ;
rep(i, , m + )
{
u = chke[i].fi, v = chke[i].se;
f[find(u)] = find(v); //一个连通块的点属于一个father
e[u].pb(mp(v, i)); //建边 i为正表示该边是反向与原来相反
e[v].pb(mp(u, -i));
}
rep(i, , n) //把一个连通块的点放到father的vector中
vec[find(i)].pb(i);
rep(i, , m + )
{
u = chke[i].fi, v = chke[i].se;
se[find(u)]++; //统计每个连通块中边的数量
}
int ans = , ans2 = ;
rep(i, , n)
{
if (find(i) != i) //每个连通块只会枚举一次
{
continue;
}
if (se[i] > SZ(vec[i])) //如果边数大于点数 则不是基环树也不是树 答案不存在
{
puts("-1 -1");
return;
}
if (se[i] < SZ(vec[i]))
{
//如果是树的话 dp两次 第一次dp出以0为根的翻转次数 第二次dp出全部点的翻转次数
int s = 1e9, s2 = ;
dp[i] = ;
int t = ;
q[] = i;
fa[i] = -;
rep(j, , t)
{
u = q[j];
for (auto p : e[u])
{
int v = p.fi;
if (v != fa[u])
{
fa[q[t++] = v] = u, pre[v] = p.se > , dp[i] += pre[v];
}
}
}
rep(j, , t)
{
u = q[j];
if (pre[u] == )
{
dp[u] = dp[fa[u]] - ;
}
else
{
dp[u] = dp[fa[u]] + ;
}
}
rep(j, , t)
s = min(s, dp[q[j]]); //当前树所需要的最少翻转次数
rep(j, , t)
if (dp[q[j]] == s)
{
s2++; //统计有多少个方案数
}
ans = ans + s;
ans2 = (ll)ans2 * s2 % mod;
}
if (se[i] == SZ(vec[i])) //基环树
{
int s = ;
for (auto u : vec[i])
{
deg[u] = SZ(e[u]); //统计每一个点的出度
}
int t = ;
for (auto u : vec[i])
if (deg[u] == )
{
q[t++] = u;
}
rep(j, , t)
{
u = q[j];
for (auto p : e[u])
{
int v = p.fi;
if (deg[v] <= )
{
continue;
}
if (p.se < )
{
s++;
}
--deg[v];
if (deg[v] == )
{
q[t++] = v;
}
}
}
int rt = -;
for (auto u : vec[i])
if (deg[u] == )
{
rt = u;
break;
}
int pree = 1e9, cnt = , u = rt, s3 = ;
while ()
{
for (auto p : e[u])
if (deg[p.fi] == && p.se + pree != )
{
s3 += p.se < ;
cnt++;
pree = p.se;
u = p.fi;
break;
}
if (u == rt)
{
break;
}
}
s3 = min(s3, cnt - s3); //环中的翻转最少次数是确定的
s += s3; //环外的方向是确定的 环外需要翻转的次数加上环中翻转的次数
ans += s;
if (s3 == cnt - s3)
{
ans2 = ans2 * % mod;
}
}
}
printf("%d %d\n", ans, ans2);
}
int main()
{
for (scanf("%d", &_); _; _--)
{
scanf("%d", &m);
solve();
}
}
//Card Game
Hdu第八场 树形dp+基环树的更多相关文章
- HDU 4514 - 湫湫系列故事——设计风景线 - [并查集判无向图环][树形DP求树的直径]
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4514 Time Limit: 6000/3000 MS (Java/Others) Memory Li ...
- hdu5293 Tree chain problem 树形dp+线段树
题目:pid=5293">http://acm.hdu.edu.cn/showproblem.php?pid=5293 在一棵树中,给出若干条链和链的权值.求选取不相交的链使得权值和最 ...
- 【BZOJ-1040】骑士 树形DP + 环套树 + DFS
1040: [ZJOI2008]骑士 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3312 Solved: 1269[Submit][Status ...
- 浅谈关于树形dp求树的直径问题
在一个有n个节点,n-1条无向边的无向图中,求图中最远两个节点的距离,那么将这个图看做一棵无根树,要求的即是树的直径. 求树的直径主要有两种方法:树形dp和两次bfs/dfs,因为我太菜了不会写后者这 ...
- 树形DP 学习笔记(树形DP、树的直径、树的重心)
前言:寒假讲过树形DP,这次再复习一下. -------------- 基本的树形DP 实现形式 树形DP的主要实现形式是$dfs$.这是因为树的特殊结构决定的——只有确定了儿子,才能决定父亲.划分阶 ...
- HDU 5293 Annoying problem 树形dp dfs序 树状数组 lca
Annoying problem 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5293 Description Coco has a tree, w ...
- POJ 2342 &&HDU 1520 Anniversary party 树形DP 水题
一个公司的职员是分级制度的,所有员工刚好是一个树形结构,现在公司要举办一个聚会,邀请部分职员来参加. 要求: 1.为了聚会有趣,若邀请了一个职员,则该职员的直接上级(即父节点)和直接下级(即儿子节点) ...
- HDU 1520 Anniversary party [树形DP]
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1520 题目大意:给出n个带权点,他们的关系可以构成一棵树,问从中选出若干个不相邻的点可能得到的最大值为 ...
- hdu 1520Anniversary party(简单树形dp)
Anniversary party Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others ...
随机推荐
- 基于durid的JDBCUtils工具类
1.JDBCUtils类 package com.alphajuns.utils; import com.alibaba.druid.pool.DruidDataSourceFactory; impo ...
- SQL中group by使用
多条重复记录,有一个字段可以进行区分,从重复的记录中取出其中一条,可利用group by字段 举例如下: 查询所有 查询地区中的最高分
- Mac搭建学习PHP环境
在sublime text 3中学习PHP,编写PHP代码: 使用的xampp开发环境: 第一步,就是安装xampp,这个没啥可说的,根据自己的系统下载安装就好,我的是OSX;第二步,就是用XAMPP ...
- body和document的梗
http://bbs.zhinengshe.com/thread-1199-1-1.html 1. 在空白的页面加点击事件,是加在body上么 ? <!DOCTYPE html> < ...
- python基础知识(字符串)
定义字符串 ' '单引号 " "双引号 只能用于单行 '" '"三引号 可以用于多行 拼接字符串使用 +号链接 字符串只能链接字符串其他类型字符串需要用s ...
- 【神经网络与深度学习】什么是HDF
什么是HDF HDF 是用于存储和分发科学数据的一种自我描述.多对象文件格式.HDF 是由美国国家超级计算应用中心(NCSA)创建的,以满足不同群体的科学家在不同工程项目领域之需要.HDF 可以表示出 ...
- 【Linux开发】编写属于你的第一个Linux内核模块
曾经多少次想要在内核游荡?曾经多少次茫然不知方向?你不要再对着它迷惘,让我们指引你走向前方-- 内核编程常常看起来像是黑魔法,而在亚瑟 C 克拉克的眼中,它八成就是了.Linux内核和它的用户空间是大 ...
- JAVA -数据类型与表达式---表达式
表达式由一个以上的运算符和操作数按一定规则组合而成,通常用于完成计算.计算结果一般是一个数值,但也不一定总是数值.用于计算的操作数可能是数值常量.符号常量.变量或其他某种类型的数据.计算和使用表达式的 ...
- [python] 格式化方法 format
先介绍包含的所有规则 花括号声明{}:用于渲染前的参数引用声明,花括号里可以用数字代表引用参数的序号,或者变量名直接引用. 从format参数引入的变量名 冒号: 字符位数声明 空白自动填补符的声明 ...
- # Excel批量处理数据
Excel批量处理数据 拖住框下拉即可得到每行+3的结果