题目传送门


题目描述

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


输入格式

每个测试点有多组测试数据。
第一行有一个正整数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. 四、Zabbix-zabbix agent部署

    1.添加zabbix安装源 rpm -i http://repo.zabbix.com/zabbix/3.4/rhel/7/x86_64/zabbix-release-3.4-2.el7.noarch ...

  2. sql limit order by and where

    1 sql limit limit size,返回前size行. limit offset , size,返回offset开始的size行,offset从0行开始. 2 sql limit with ...

  3. Mysql 5.7存储过程的学习

    存储过程:对sql的封装和重用,经编译创建并保存在数据库中,通过指定存储过程的名字并给定参数(需要时)来调用执行. 优缺点: (1) 优点: 执行速度快------存储过程只在创建时进行编译,以后每次 ...

  4. C++中的class和struct区别

    1,经过不停的改进,结构体 struct 变得原来越不像它在 C 语言中的样子了: 1,struct 在 C 语言中仅为了定义一个变量的集合,仅此而已,不能定义函数: 2,struct 在 C++ 中 ...

  5. 偶遇com组件 .rc 文件 not found .tlb文件问题:

    好久木有弄com组件来,记忆已经模糊了,今天遇见一个编译问题,折腾了半天,mark一下: xxx_x64.rc(273): error RC2135: file not found: xxx.tlb ...

  6. centos7使用kubeadm搭建kubernetes集群

    一.本地实验环境准备 服务器虚拟机准备 IP CPU 内存 hostname 192.168.222.129 >=2c >=2G master 192.168.222.130 >=2 ...

  7. hive中的索引创建

    1.在hive中创建索引所在表 create table if not exists h_odse.hxy(id int,name string,hobby array<string>,a ...

  8. tensorflow学习笔记七----------卷积神经网络

    卷积神经网络比神经网络稍微复杂一些,因为其多了一个卷积层(convolutional layer)和池化层(pooling layer). 使用mnist数据集,n个数据,每个数据的像素为28*28* ...

  9. WindowsForms使用Telerik Reporting

    新建一个WindowsForms窗体项目 然后拖动ReportViewer这个控件到WindowsForms的窗体中 如上图所示,用来呈现报表的控件,这个控件可以打印报表,转换报表这类的功能 接下来我 ...

  10. 常用css相关笔记

    最后一个css不加样式 .nav-sort li:not(:last-child) { border-bottom:#3e3e3e 1px solid; } 垂直居中 vertical-align: ...