[HDU6403]:Card Game(dfs+DP+基环树)
题目传送门
题目描述
她依然在我不知道的地方做我不知道的事。
桌面上摊开着一些卡牌,这是她平时很爱玩的一个游戏。如今卡牌还在,她却不在我身边。不知不觉,我翻开了卡牌,回忆起了当时一起玩卡牌的那段时间。
每张卡牌的正面与反面都各有一个数字,我每次把卡牌按照我想的放到桌子上,而她则是将其中的一些卡牌翻转,最后使得桌面上所有朝上的数字都各不相同。
我望着自己不知不觉翻开的卡牌,突然想起了之前她曾不止一次的让我帮她计算最少达成目标所需要的最少的翻转次数,以及最少翻转达成目标的方案数。
(两种方式被认为是相同的当且仅当两种方式需要翻转的卡牌的集合相同)
如果我把求解过程写成程序发给她,她以后玩这个游戏的时候会不会更顺心一些?
输入格式
每个测试点有多组测试数据。
第一行有一个正整数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+基环树)的更多相关文章
- Hdu第八场 树形dp+基环树
Card Game 每个牌背面的数字朝正面的数字连一条有向边 则题目变为问你最少翻转多少次 能使得每个数字的入度不超过1 首先判断图中每个连通块是不是树或者基环树 因为只有树或者基环树能使得每个点的入 ...
- Luogu P2081 [NOI2012]迷失游乐园 | 期望 DP 基环树
题目链接 基环树套路题.(然而各种错误调了好久233) 当$m=n-1$时,原图是一棵树. 先以任意点为根做$dp$,求出从每一个点出发,然后只往自己子树里走时路径的期望长度. 接着再把整棵树再扫一遍 ...
- bzoj 2878: [Noi2012]迷失游乐园【树上期望dp+基环树】
参考:https://blog.csdn.net/shiyukun1998/article/details/44684947 先看对于树的情况 设d[u]为点u向儿子走的期望长度和,du[u]为u点的 ...
- [luogu2081 NOI2012] 迷失游乐园 (树形期望dp 基环树)
传送门 题目描述 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩. 进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多有一个环(即m ...
- BZOJ 1040 [ZJOI2008]骑士 (基环树+树形DP)
<题目链接> 题目大意: Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的 ...
- HDU6403 Card Game【基环树 + 树形DP】
HDU6403 Card Game 题意: 给出\(N\)张卡片,卡片正反两面都有数字,现在要翻转一些卡片使得所有卡片的正面的值各不相同,问最小翻转次数和最小翻转情况下的不同方案数 \(N\le 10 ...
- 洛谷AT2046 Namori(思维,基环树,树形DP)
洛谷题目传送门 神仙思维题还是要写点东西才好. 树 每次操作把相邻且同色的点反色,直接这样思考会发现状态有很强的后效性,没办法考虑转移. 因为树是二分图,所以我们转化模型:在树的奇数层的所有点上都有一 ...
- bzoj1791[IOI2008]Island岛屿(基环树+DP)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1791 题目大意:给你一棵n条边的基环树森林,要你求出所有基环树/树的直径之和.n< ...
- bzoj1040 基环树森林dp
https://www.lydsy.com/JudgeOnline/problem.php?id=1040 Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社 ...
随机推荐
- 【6.10校内test】T3 加分二叉树
加分二叉树[题目链接] 感觉我超废 这道题当时压根就不会qwq(我倒是挺适合写rand的qwq) 对于暴力的做法: 输入数据,定义数组men[i][i]=v[i](输入的第二行): dfs: dfs ...
- Windows 运行时加载动态库
下面是一个运行时加载nvcuda.dll,并检测当前驱动版本最大支持的CUDA版本的例子. #include "cuda.h" #include <stdio.h> # ...
- PHP实现支付宝小程序用户授权的工具类
背景 最近项目需要上线支付宝小程序,同时需要走用户的授权流程完成用户信息的存储,以前做过微信小程序的开发,本以为实现授权的过程是很简单的事情,但是再实现的过程中还是遇到了不少的坑,因此记录一下实现的过 ...
- 模板 - 无旋Treap
一般而言作为一棵平衡树只需要插入,删除,值求排名,排名求值,前驱,后继,六个接口. #include<bits/stdc++.h> using namespace std; typedef ...
- JUNIT4 GroboUtils多线程测试
阅读更多 利用JUNIT4,GroboUtils进行多线程测试 多线程编程和测试一直是比较难搞的事情,特别是多线程测试.只用充分的测试,才可以发现多线程编码的潜在BUG.下面就介绍一下我自己在测试多线 ...
- mysql,oracle,sql server数据库默认的端口号,端口号可以为负数吗?以及常用协议所对应的缺省端口号
mysql,oracle,sql server数据库默认的端口号? mysql:3306 Oracle:1521 sql server:1433 端口号可以为负吗? 不可以,端口号都有范围的,0~65 ...
- python学习笔记(2):科学计算及数据可视化入门
一.NumPy 1.NumPy:Numberical Python 2.高性能科学计算和数据分析的基础包 3.ndarray,多维数组(矩阵),具有矢量运算的能力,快速.节省空间 (1)ndarray ...
- vue 模拟去哪网
模拟项目中遇到的问题,总结如下: 1.争对轮播图 使用vue-awesome-swiper npm install vue-awesome-swiper@2.6.7 --save //因为此版本稳定 ...
- HTTPS原理以及流程
一.HTTP和HTTPS的区别 HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全. HTTPS协议是由SSL+HTTP协议构建的可进行加密传输.身份认证的网 ...
- CentOS7搭建FastDFS+Nginx
1. FastDFS 介绍 FastDFS是一个开源的分布式文件系统,她对文件进行管理,功能包括:文件存储.文件同步.文件访问(文件上传.文件下载)等,解决了大容量存储和负载均衡的问题.特别适合以文件 ...