Card Game

每个牌背面的数字朝正面的数字连一条有向边

则题目变为问你最少翻转多少次 能使得每个数字的入度不超过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+基环树的更多相关文章

  1. HDU 4514 - 湫湫系列故事——设计风景线 - [并查集判无向图环][树形DP求树的直径]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4514 Time Limit: 6000/3000 MS (Java/Others) Memory Li ...

  2. hdu5293 Tree chain problem 树形dp+线段树

    题目:pid=5293">http://acm.hdu.edu.cn/showproblem.php?pid=5293 在一棵树中,给出若干条链和链的权值.求选取不相交的链使得权值和最 ...

  3. 【BZOJ-1040】骑士 树形DP + 环套树 + DFS

    1040: [ZJOI2008]骑士 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3312  Solved: 1269[Submit][Status ...

  4. 浅谈关于树形dp求树的直径问题

    在一个有n个节点,n-1条无向边的无向图中,求图中最远两个节点的距离,那么将这个图看做一棵无根树,要求的即是树的直径. 求树的直径主要有两种方法:树形dp和两次bfs/dfs,因为我太菜了不会写后者这 ...

  5. 树形DP 学习笔记(树形DP、树的直径、树的重心)

    前言:寒假讲过树形DP,这次再复习一下. -------------- 基本的树形DP 实现形式 树形DP的主要实现形式是$dfs$.这是因为树的特殊结构决定的——只有确定了儿子,才能决定父亲.划分阶 ...

  6. HDU 5293 Annoying problem 树形dp dfs序 树状数组 lca

    Annoying problem 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5293 Description Coco has a tree, w ...

  7. POJ 2342 &&HDU 1520 Anniversary party 树形DP 水题

    一个公司的职员是分级制度的,所有员工刚好是一个树形结构,现在公司要举办一个聚会,邀请部分职员来参加. 要求: 1.为了聚会有趣,若邀请了一个职员,则该职员的直接上级(即父节点)和直接下级(即儿子节点) ...

  8. HDU 1520 Anniversary party [树形DP]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1520 题目大意:给出n个带权点,他们的关系可以构成一棵树,问从中选出若干个不相邻的点可能得到的最大值为 ...

  9. hdu 1520Anniversary party(简单树形dp)

    Anniversary party Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others ...

随机推荐

  1. 【Web】[原创]ie6,7中td和img之间有间隙

    情形描述 开发工具:VS2010: 浏览器版本:IE6以上,火狐,谷歌: 页面布局设计:Table+Img布局: 项目预览问题:火狐,谷歌,IE8以上未出现问题,IE6,IE7图片之间有间隙. 分析原 ...

  2. 利用js代码自动删除稿件的普通弹幕

    事情的起因是在b站投稿了一个高级弹幕测试的视频(av9940487),但是由于b站的弹幕池机制是新的弹幕顶掉旧的弹幕,所以导致一些人发的高级弹幕很快就被顶掉了. 所以就想着写个脚本来自动删除属性为普通 ...

  3. oracle_协议适配器错误_协议适配器不可加载_TNS监听程序在connect-data中未获得service-name

    最近在使用powerdesigner连接远程oracle进行反向工程操作时,出现了一些问题,这些问题很普遍,大多是由于配置引起的 说明:(1)远程数据库版本问 oracle11g 64bit (2)本 ...

  4. 为什么样本标准差要除以n-1?

    根据中心极限定理,我们可以用样本估计总体的平均值.那么,如果通过n个样本估计总体的标准差则需要除以n-1,这是为什么呢? 标准差是描述数据与平均值的偏离程度. 而因为样本的数据量比总体的数据量少,因此 ...

  5. Ubuntu强制修改root密码

    Ubuntu忘记超级用户root密码并重新设置密码 解决方法如下: 第一种方法——不进入recovery mode设置(推荐) 说明案例:Ubuntu版本:Ubuntu 16.04.3 LTS Ste ...

  6. VS显示代码行号

    工具>文本编辑器>常规    如图勾选行号选项,点击确定即可显示行号. 如果没有行号的选项,则需先勾选下图中左下角的‘’显示所有设置选‘’‘项.

  7. selenium—显示等待中期望的场景语句

    ① alert_is_present() 判断页面是否出现alert框 wait = WebDriverWait(driver,10) alert = wait.until(EC.alert_is_p ...

  8. [转帖]从Intel和ARM争霸,谈芯片前世今生

    从Intel和ARM争霸,谈芯片前世今生 http://www.itpub.net/2019/07/24/2476/ 长文预警, 写的非常好.. 我尽量写得轻松一些,因为其实这个话题很有趣,仔细探究起 ...

  9. PHP,mysql,nginxunx中安装

    一:安装PHP,mysql,nginx linux装软件方式: 1.源码安装:下载wget-->解压tar -zxvf -->配置 ./configure --->编译make -- ...

  10. 数据库or、in、<>、>=、<=、butween区别

    操作前先关闭数据库缓存 #创建测试的test表 DROP TABLE IF EXISTS test; CREATE TABLE test( `id` ) NOT NULL, `name` ) DEFA ...