[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国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社 ...
随机推荐
- CentOS7编译安装sshpass过程
环境说明:centos 7 cat /etc/redhat-release CentOS Linux release 7.6.1810 (Core) 我的sshpass版本 sshpass-1.06. ...
- springboot + mybaits + oracle 项目
1.pom设置 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="htt ...
- PostgreSQL dblink使用过程
安装: 进入/root/postgresql-11.2/contrib/dblink make && make install 切换到postgres用户 [root@fce40690 ...
- CentOS7 内核模块管理
1.查看所有模块:lsmod 2.查看指定模块的详细信息:modinfo 模块名 3.动态加载模块:modprobe 模块名 4.动态卸载模块:modprobe -r 模块名 5.开机自动加载模块:假 ...
- 剑指Offer编程题(Java实现)——链表中倒数第k个结点
题目描述 输入一个链表,输出该链表中倒数第k个结点. 注意: 该题目不可以用先反转链表再输出第k个结点的方式,因为反转链表会改变该结点的next指向 思路一 使用栈Stack倒序存储,顺序pop第k个 ...
- Aliyun-Centos 7 LNMP安装(最新版LNMP)
linux装软件方式:1.源码安装:下载wget-->解压tar -zxvf -->配置 ./configure --->编译make -->安装 make install 2 ...
- 17: VUE数据绑定 与 Object.defineProperty
VUE数据绑定原理:https://segmentfault.com/a/1190000006599500?utm_source=tag-newest Object.defineProperty(): ...
- 六、JVM — JDK 监控和故障处理工具
JDK 监控和故障处理工具总结 JDK 命令行工具 jps:查看所有 Java 进程 jstat: 监视虚拟机各种运行状态信息 jinfo: 实时地查看和调整虚拟机各项参数 jmap:生成堆转储快照 ...
- PHP实现支付宝小程序用户授权的工具类
背景 最近项目需要上线支付宝小程序,同时需要走用户的授权流程完成用户信息的存储,以前做过微信小程序的开发,本以为实现授权的过程是很简单的事情,但是再实现的过程中还是遇到了不少的坑,因此记录一下实现的过 ...
- ThinkPHP中的display()和fetch()的区别
fetch()传入的参数是模板名,用模板文件来输出; display()传入的是字符串,输出传递的内容.