题目传送门


题目描述

她依然在我不知道的地方做我不知道的事。
桌面上摊开着一些卡牌,这是她平时很爱玩的一个游戏。如今卡牌还在,她却不在我身边。不知不觉,我翻开了卡牌,回忆起了当时一起玩卡牌的那段时间。
每张卡牌的正面与反面都各有一个数字,我每次把卡牌按照我想的放到桌子上,而她则是将其中的一些卡牌翻转,最后使得桌面上所有朝上的数字都各不相同。
我望着自己不知不觉翻开的卡牌,突然想起了之前她曾不止一次的让我帮她计算最少达成目标所需要的最少的翻转次数,以及最少翻转达成目标的方案数。
(两种方式被认为是相同的当且仅当两种方式需要翻转的卡牌的集合相同)
如果我把求解过程写成程序发给她,她以后玩这个游戏的时候会不会更顺心一些?


输入格式

每个测试点有多组测试数据。
第一行有一个正整数T表示数据组数。
接下来对于每组数据,第一行有一个正整数n,表示桌子上卡牌的数目。
接下来有n行,每行两个整数x,y, 表示一张两面分别是x,y的卡牌,并且当前朝上的数字为x。


输出格式

对于每组测试数据,输出一行两个整数,分别代表最小的翻转次数、方案数(方案数对998244353取模)。
如果无解则输出“-1 -1”。


样例

样例输入

3
4
1 2
1 3
4 5
4 6
2
1 1
1 1
3
1 2
3 4
5 6

样例输出

2 4
-1 -1
0 1


数据范围与提示

对于20%的数据:

$n \leqslant 20$

$T=1$

对于100%的数据:

$n \leqslant {10}^5$

$T \leqslant 50$

$1 \leqslant x,y \leqslant 2n$


题解

看到这道题,你一定想到了用某种数学方法解决,那么你就凉了。

这么来考虑,将卡片的正面数字向反面数字连边,每次翻转卡牌就相当于是将边反向,而我们的目的就是让图中的每一个联通块内所有点的出度最多为1。

而这时,就分为三种情况:

  1.如果图中有一个联通块内的边数大于点数,那么显然不可没能满足,则直接输出“-1 -1”即可。

  2.如果图中有一个联通块内的边数等于点数,则这个联通块是一个基环树,那么我们就分别顺时针和逆时针跑这个联通快,看那个方向需要反转变得次数更少,如果两个方向答案相同,则方案数为2,否则为1。

  3.如果图中有一个联通块内的边数等于点数-1,则这个联通块是一棵树,那么就考虑使用DP统计答案,但是显然我们需要用这棵树内所有的点为根跑一遍,暴力跑的话时间复杂度显然很高;但是我们发现,对于每一次换根只有它和它的父亲之间的连边会对答案造成影响,而且智慧是上一个答案的+1或是-1,这样我们就可以在时间允许的范围内计算答案了。


代码时刻

#include<bits/stdc++.h>
using namespace std;
struct rec
{
int nxt;
int to;
bool w;//边权为1表示从正面连向反面
}e[200002];
int n;
int head[200001],cnt;
int b,d;
bool vis[200001];
int st,ed,po;
int sta[200001],top;
long long dp[200001],flag[200001];
long long ans,sum,num;
void pre_work()//多测不清空,爆零两行泪TAT……
{
ans=0;
sum=cnt=1;
memset(head,0,sizeof(head));
memset(vis,0,sizeof(vis));
memset(dp,0,sizeof(dp));
memset(flag,0,sizeof(flag));
}
void add(int x,int y,int w)//建边
{
e[++cnt].nxt=head[x];
e[cnt].to=y;
e[cnt].w=w;
head[x]=cnt;
}
void pre_dfs(int x)//跑每一个联通块,计算点数和边数
{
d++;
vis[x]=1;
for(int i=head[x];i;i=e[i].nxt)
{
b++;
if(!vis[e[i].to])
pre_dfs(e[i].to);
}
}
void dfs(int x,int fa)//DP前对每一个联通块进行预处理
{
flag[x]=0;
vis[x]=1;
for(int i=head[x];i;i=e[i].nxt)
{
if(e[i].to==fa)continue;
if(!vis[e[i].to])
{
dfs(e[i].to,x);
flag[x]+=flag[e[i].to]+e[i].w;
}
else
{
st=x;
ed=e[i].to;
po=i;
}
}
}
void pro_dfs(int x,int fa)//DP统计答案
{
sta[++top]=dp[x];
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa&&i!=po&&i!=(po^1))
{
dp[e[i].to]=dp[x]+(e[i].w?-1:1);//换根之后的+1或者-1
pro_dfs(e[i].to,x);
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
pre_work();
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y,1);
add(y,x,0);
}
for(int i=1;i<=2*n;i++)
if(!vis[i])
{
b=d=0;
pre_dfs(i);
if(b/2>d)//判断是否有解
{
puts("-1 -1");
goto nxt;//直接跳到结尾,和continue功能类似,但是可以指定跳转位置
}
}
memset(vis,0,sizeof(vis));
for(int i=1;i<=2*n;i++)
{
if(!vis[i])
{
st=ed=po=top=0;
num=1;
dfs(i,0);
dp[i]=flag[i];
pro_dfs(i,0);
if(!st)//如果是一棵树
{
sort(sta+1,sta+top+1);
for(int j=2;j<=top;j++)
{
if(sta[j]!=sta[1])break;
num++;
}
ans+=sta[1];
}
else//是一棵基环树
{
po%=2;
if(dp[st]+po==dp[ed]+(po^1))num=2;
else num=1;
ans+=min(dp[st]+po,dp[ed]+(po^1));
}
sum=sum*num%998244353;
}
}
printf("%lld %lld\n",ans,sum);
nxt:;//上面的"goto nxt;"跳转到的位置
}
return 0;
}

rp++

[HDU6403]:Card Game(dfs+DP+基环树)的更多相关文章

  1. Hdu第八场 树形dp+基环树

    Card Game 每个牌背面的数字朝正面的数字连一条有向边 则题目变为问你最少翻转多少次 能使得每个数字的入度不超过1 首先判断图中每个连通块是不是树或者基环树 因为只有树或者基环树能使得每个点的入 ...

  2. Luogu P2081 [NOI2012]迷失游乐园 | 期望 DP 基环树

    题目链接 基环树套路题.(然而各种错误调了好久233) 当$m=n-1$时,原图是一棵树. 先以任意点为根做$dp$,求出从每一个点出发,然后只往自己子树里走时路径的期望长度. 接着再把整棵树再扫一遍 ...

  3. bzoj 2878: [Noi2012]迷失游乐园【树上期望dp+基环树】

    参考:https://blog.csdn.net/shiyukun1998/article/details/44684947 先看对于树的情况 设d[u]为点u向儿子走的期望长度和,du[u]为u点的 ...

  4. [luogu2081 NOI2012] 迷失游乐园 (树形期望dp 基环树)

    传送门 题目描述 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩. 进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多有一个环(即m ...

  5. BZOJ 1040 [ZJOI2008]骑士 (基环树+树形DP)

    <题目链接> 题目大意: Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的 ...

  6. HDU6403 Card Game【基环树 + 树形DP】

    HDU6403 Card Game 题意: 给出\(N\)张卡片,卡片正反两面都有数字,现在要翻转一些卡片使得所有卡片的正面的值各不相同,问最小翻转次数和最小翻转情况下的不同方案数 \(N\le 10 ...

  7. 洛谷AT2046 Namori(思维,基环树,树形DP)

    洛谷题目传送门 神仙思维题还是要写点东西才好. 树 每次操作把相邻且同色的点反色,直接这样思考会发现状态有很强的后效性,没办法考虑转移. 因为树是二分图,所以我们转化模型:在树的奇数层的所有点上都有一 ...

  8. bzoj1791[IOI2008]Island岛屿(基环树+DP)

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1791 题目大意:给你一棵n条边的基环树森林,要你求出所有基环树/树的直径之和.n< ...

  9. bzoj1040 基环树森林dp

    https://www.lydsy.com/JudgeOnline/problem.php?id=1040 Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社 ...

随机推荐

  1. 关于MySQL的安装使用心得

    MySQL浅浅地学习了几天,当然还是转到正轨Java上来了,昨天打了一串代码,测试注解来着,结果MySQL挂了~~~ 如何干净卸载MySQL帖子有很多,不再赘述,注册表是个好东西~~ 卸载了Mysql ...

  2. [转帖]同事推荐的的aira2

    Windows系统安装最新版Aria2客户端及使用教程 https://www.moerats.com/archives/519/ 改天学习一下. 说明:之前都是说的在Linux VPS服务器上安装A ...

  3. [转帖]Oracle dba_objects和all_objects 最大的区别

    Oracle dba_objects和all_objects 最大的区别 原创 Oracle 作者:maohaiqing0304 时间:2015-08-14 15:07:18  9281  0   链 ...

  4. [19/06/05-星期三] CSS基础_样式表的位置(内联、内部、外部样式)、块元素(div)、内联元素(span)、常用的选择器

    一.概念 CSS(Cascading Style Sheets,层叠样式表) 可以用来为网页创建样式表,通过样式表可以对网页进行装饰. 所谓层叠,就是可以将整个网页想象成是一层一层的结构,层次高的将会 ...

  5. 浅拷贝&深拷贝

    浅拷贝新的对象指向原来对象的地址 深拷贝新的对象中,原来是可变对象,会新复制一份值指向新的地址[11,22,33]若原来的对象里含有可变对象,里面的这个可变对象也会指向新的地址['qwer', 123 ...

  6. 解决MySql忘记密码

    描述:忘记了mysql的登录密码,无法登录的情况下该怎么办? 环境:CentOS 7,数据库:mysql 5.7 1.停止数据库(先查看mysql服务是否运行) # ps -ef | -i grep ...

  7. PHP排序函数sort、rsort、asort、arsort、ksort、krsort

    1.sort函数用于对数组元素值从低到高排序,去除原始索引元素,重新生成0,1,2..的键2.rsort函数用于对数组元素值从高到低排序,去除原始索引元素,重新生成0,1,2..的键3.asort函数 ...

  8. UITextField 文本框 只能输入数字 且保留2位小数 实现

    http://blog.sina.com.cn/s/blog_aa7579f601015xvx.html #pragma mark - #pragma mark UITextField - (BOOL ...

  9. 前端开发HTML&css入门——CSS&选择器练习

    CSS 层叠样式表 (Cascading Style Sheets)css可以用来为网页创建样式表,通过样式表可以对网页进行装饰.所谓层叠,可以将整个网页想象成是一层一层的结构,层次高的将会覆盖层次低 ...

  10. new/delete、malloc/free 的区别

    相同点 1.都可以动态的申请或释放内存 2.存储方式相同,动态申请的内存都存放在堆中,无法自动释放,要调用对应的delete和free   区别 1.new能自行的计算需要分配的空间,malloc需要 ...