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 ...
随机推荐
- k8s报错解决
. Jul :: lab3 etcd[]: the server is already initialized as member before, starting as etcd member... ...
- [转] Maven更新父子模块的版本号, mvn versions:set
[From]https://www.cnblogs.com/ilovexiao/p/5663761.html 前置条件: 1.安装有吃饭的家伙JAVA和MAVEN. 首先,需要有一个packaging ...
- js中的堆内存和栈内存
我们常常会听说什么栈内存.堆内存,那么他们到底有什么区别呢,在js中又是如何区分他们的呢,今天我们来看一下. 一.栈内存和堆内存的区分 一般来说,栈内存主要用于存储各种基本类型的变量,包括Boolea ...
- 【SVN】导出项目后报错汇总
原文链接 1.jsp页面内:标点符号,引入报错 解决方法:关闭此项目的jsp验证,右键,最下面一个,Verification,右边一溜只留一个dtd就好 2. 编码问题-乱码 刚拉下来的项目编码可能与 ...
- HBITMAP与BITMAP 的区别
HBITMAP 是句柄: BITMAP 是实例: typedef struct tagBITMAP { LONG bmType; ...
- python 将视频转换成音频
安装库 sudo pip install moviepy 代码 index.py from moviepy.editor import * video = VideoFileClip('test.mp ...
- 【Java】递归删除目录以及文件
public static void deleteDirectory(String path) { File pFile = new File(path); //若目录以及文件不存在,则终止继续执行方 ...
- python-https状态码
HTTP状态码状态码的职责是当客户端向服务器发送请求时,描述返回的请求结果.借助状态码,用户可以知道服务器端是正常处理了请求,还是出现了错误. 状态码的类别 >>>状态码 类别 说明 ...
- Java生成二维码连接
本文使用的是Goodge的zxing 添加maven依赖 <dependency> <groupId>com.google.zxing</groupId> < ...
- __VA_ARGS__用法
转载 自定义调试信息的输出 调试信息的输出方法有很多种, 例如直接用printf, 或者出错时使用perror, fprintf等将信息直接打印到终端上, 在Qt上面一般使用qDebug,而守护进 ...