link

Description

桌面上摊开着一些卡牌,这是她平时很爱玩的一个游戏。如今卡牌还在,她却不在我身边。不知不觉,我翻开了卡牌,回忆起了当时一起玩卡牌的那段时间。

每张卡牌的正面与反面都各有一个数字,我每次把卡牌按照我想的放到桌子上,而她则是将其中的一些卡牌翻转,最后使得桌面上所有朝上的数字都各不相同。

我望着自己不知不觉翻开的卡牌,突然想起了之前她曾不止一次的让我帮她计算最少达成目标所需要的最少的翻转次数,以及最少翻转达成目标的方案数。

(两种方式被认为是相同的当且仅当两种方式需要翻转的卡牌的集合相同)

如果我把求解过程写成程序发给她,她以后玩这个游戏的时候会不会更顺心一些?

\(n\le 10^5\)

Solution

题目倒不是很难,不过有很多坑点,所以在这里记录一下,以示后人。

首先想到,我们可以将 \(y\to x\) 连上一条边,那么答案就是对于每一个连通块通过转变边的方向使得每个点入度 \(\le 1\) 即该连通块是个基环树或者树的最小操作数。那么判断无解就很简单了,只需要判断在无向边情况下是否是一个基环树或者树。

考虑怎么求最小操作数,可以发现基环树的话,树的部分如何确定方向是固定的,可以统计它是逆方向的边的个数,而在基环树上则可以全都成顺方向或者全都成逆方向,这个比较两者大小即可。不过需要注意的是,如果你是统计的在环上的边的颜色,二元环的时候可能需要特殊判断一下。

对于树的话,你发现答案就是对于每个点为根的时候逆方向的边的个数的最小值,然后这个东西可以换根 dp。

复杂度 \(\Theta(n)\)。

Code

#include <bits/stdc++.h>
using namespace std; #define Int register int
#define inf 1000000000
#define mod 998244353
#define MAXN 200005 template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);} int T,n,qX[MAXN],qY[MAXN],tot[MAXN]; int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
void Add (int &a,int b){a = add (a,b);}
void Sub (int &a,int b){a = dec (a,b);} struct node{
int minv,sum;
node(){minv = inf,sum = 1;}
node(int _minv,int _sum){minv = _minv,sum = _sum;}
node operator + (const node &p)const{
if (minv == p.minv) return node(minv,add (sum,p.sum));
else if (minv < p.minv) return *this;
else return p;
}
node operator * (const node &p)const{return node(minv + p.minv,mul (sum,p.sum));}
}; struct edge{
int v,w,nxt;
}e[MAXN << 1];
int toop = 1,head[MAXN]; void link (int u,int v,int w){
e[++ toop] = edge {v,w,head[u]},
head[u] = toop;
} bool vis[MAXN];
vector <int> pnt;
int se,sp,par[MAXN],nxt[MAXN]; void dfs (int u,int fa){
vis[u] = 1,sp ++,pnt.push_back (u);
for (Int i = head[u];i;i = e[i].nxt){
se ++;
if (i != (fa ^ 1)){
int v = e[i].v;
if (!vis[v]) dfs (v,i);
}
}
} int sum[2];
bool rnm[MAXN];
vector <int> cir; void dfs1 (int u,int fa){
vis[u] = 1;
for (Int i = head[u];i;i = e[i].nxt) if (i != (fa ^ 1)){
int v = e[i].v;
if (vis[v]){
if (cir.size()) continue;
int tmp = u;cir.push_back (u);
while (tmp ^ v) tmp = par[tmp],cir.push_back (tmp);
for (Int s : cir) rnm[s] = 1;
for (Int s = 0;s < cir.size() - 1;++ s) nxt[cir[s]] = cir[s + 1];
nxt[cir.back ()] = cir[0];
}
else par[v] = u,dfs1 (v,i);
}
} int f1[MAXN],f2[MAXN],siz[MAXN];
void dfs2 (int u,int fa){
f1[u] = 0,siz[u] = vis[u] = 1;
for (Int i = head[u];i;i = e[i].nxt) if (i != (fa ^ 1)){
int v = e[i].v;
if (!vis[v]) dfs2 (v,i),siz[u] += siz[v],f1[u] += f1[v] + (e[i].w == 1);
}
} void dfs3 (int u,int fa){
int all = 0;
for (Int i = head[u];i;i = e[i].nxt) if (i != (fa ^ 1)) all += f1[e[i].v] + (e[i].w == 1);
for (Int i = head[u];i;i = e[i].nxt) if (i != (fa ^ 1)){
int v = e[i].v,w = e[i].w;
f2[v] = f2[u] + (w == 0) + all - (f1[v] + (w == 1)),dfs3 (v,i);
}
} #define pii pair<int,int>
#define ppi pair<pii,int>
vector <ppi> fuck; void dfs4 (int u,int fa){
vis[u] = 1;
for (Int i = head[u];i;i = e[i].nxt) if (i != (fa ^ 1)){
int v = e[i].v,w = e[i].w;
if (v == nxt[u]){
sum[w] ++;
if (cir.size() == 2) fuck.push_back ({{u,v},w});
if (v == cir[0]) return ;
else dfs4 (v,i);
}
}
} void Work (){
read (n),toop = 1,cir.clear (),pnt.clear (),se = sp = 0,memset (sum,0,sizeof (sum));
for (Int i = 1;i <= 2 * n;++ i) vis[i] = tot[i] = f1[i] = f2[i] = nxt[i] = siz[i] = rnm[i] = par[i] = head[i] = 0;
for (Int i = 1;i <= n;++ i){
read (qX[i],qY[i]);
if (qX[i] == qY[i]) tot[qX[i]] ++;
link (qY[i],qX[i],0),link (qX[i],qY[i],1);
}
for (Int i = 1;i <= 2 * n;++ i)
if (tot[i] >= 2){
puts ("-1 -1");
return ;
}
else tot[i] = 0;
node ans = node{0,1};
for (Int i = 1;i <= 2 * n;++ i) if (!vis[i]){
cir.clear ();
se = sp = 0,dfs (i,-1),se /= 2;
if (se > sp){
puts ("-1 -1");
return ;
}
for (Int u : pnt) vis[u] = 0;
memset (sum,0,sizeof (sum)),dfs1 (i,0);
node ts;
if (se == sp){
ts = node(0,1);
for (Int u : pnt) vis[u] = 0;for (Int u : cir) vis[u] = 1;
int sht = 0;
for (Int u : cir) dfs2 (u,0),ts = ts * node(f1[u],1),sht += f1[u];
fuck.clear(),dfs4 (cir[0],0);
if (cir.size() == 2){
for (Int s1 = 0;s1 < 4;++ s1) if (fuck[s1].second == 0) tot[fuck[s1].first.second] ++;
if (tot[fuck[0].first.first] != 1) sum[0] = sum[1] = 1;
else sum[0] = 2,sum[1] = 0;
}
else ;
ts = ts * node(min (sum[0],sum[1]),sum[0] == sum[1] ? 2 : 1);
}
else{
for (Int u : pnt) vis[u] = 0;
f2[i] = 0,dfs2 (i,0),dfs3 (i,0),ts = node(inf,0);
for (Int u : pnt) ts = ts + node(f1[u] + f2[u],1);
}
ans = ans * ts;
for (Int s : cir) rnm[s] = 0;
pnt.clear ();
}
write (ans.minv),putchar (' '),write (ans.sum),putchar ('\n');
} signed main(){
read (T);
while (T --> 0) Work ();
return 0;
}

题解 「HDU6403」卡片游戏的更多相关文章

  1. 题解 「SDOI2017」硬币游戏

    题目传送门 Description 周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利. 大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了. 同学们觉得要加强 ...

  2. 【LOJ】#2067. 「SDOI2016」硬币游戏

    题解 c一样的就是一个独立的游戏 我们对于2和3的指数 sg[i][j] 表示\(c \cdot 2^i \cdot 3^j\)的棋子,只有这个硬币是反面,翻转的硬币是正面的sg值 枚举sg函数所有可 ...

  3. 「Githug」Git 游戏通关流程

    Githug 他喵的这是个啥!?难道不是 GitHub 拼错了么,和 Git 什么关系? 和游戏又有什么关系? 其实,他的元身在这里:https://github.com/Gazler/githug  ...

  4. 【LOJ】#2182. 「SDOI2015」寻宝游戏

    题解 终于了解怎么动态维护虚树了 就是把点按照dfs序排个序啊 这道题显然是求虚树上所有边长的两倍 我们把dfs序排完序,相邻两个点加上路径长(包括首尾),删除的时候删一个点减去它到两边再加上新近相邻 ...

  5. 【LOJ】#2126. 「HAOI2015」数组游戏

    题解 简单分析一下就知道\(\lfloor \frac{N}{i} \rfloor\)相同的\(i\)的\(sg\)函数相同 所以我们只要算\(\sqrt{n}\)个\(sg\)函数就好 算每一个\( ...

  6. 【LOJ】#2562. 「SDOI2018」战略游戏

    题解 圆方树建好之后点是原来的两倍,而st表求lca也要开到点的两倍,所以是四倍 我并没有开小,然而= =,我的预处理log2,写成了200000,而不是400000 我是不是折翼啊= = 很可写,我 ...

  7. LG1640 「SCOI2010」连续攻击游戏 二分图最大匹配

    问题描述 LG1640 题解 一开始以为是把\((a,b)\)作为左右部点,发现\(n \le 1000000\),建图是\(O(n^2)\)的,会爆掉 属性值向\(i\)建边. \(\mathrm{ ...

  8. @loj - 2004@ 「SDOI2017」硬币游戏

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数 ...

  9. 【LOJ 2004】「SDOI2017」硬币游戏

    LOJ 2004 100pts 首先我们肯定要建AC自动机的.. 那么这题就肯定是个AC自动机上\(dp\). 所以想想状态. 首先如果我们把状态设成这样行不行: \(dp(i)\)表示匹配到了i节点 ...

随机推荐

  1. ScheduledExecutorService中scheduleAtFixedRate方法与scheduleWithFixedDelay方法的区别

    ScheduledExecutorService中scheduleAtFixedRate方法与scheduleWithFixedDelay方法的区别 ScheduledThreadPoolExecut ...

  2. 理解ASP.NET Core - [01] Startup

    注:本文隶属于<理解ASP.NET Core>系列文章,请查看置顶博客或点击此处查看全文目录 准备工作:一份ASP.NET Core Web API应用程序 当我们来到一个陌生的环境,第一 ...

  3. 使用Visual Studio分析dump

    最近系统是不是CPU会飙升的百分之九十多甚至百分百,在本地又很难复现问题,无法定位问题出现在哪. 可以用转储文件来保存现场,然后通过分析dump文件可以大概分析出问题的所在 生成转存文件 在CPU飙升 ...

  4. easyexcel

    导出: package com.example.demo.excel.demo0; import com.alibaba.excel.annotation.ExcelProperty; import ...

  5. Nginx 极简入门教程!(转)

    基本介绍 Nginx 是一个高性能的 HTTP 和反向代理 web 服务器,同时也提供了 IMAP/POP3/SMTP 服务. Nginx 是由伊戈尔·赛索耶夫为俄罗斯访问量第二的 Rambler.r ...

  6. Django自带评论功能的基本使用

    1. 模块安装 pip install django-contrib-comments 2. 注册APP INSTALLED_APP=( #..., 'django_comments', 'djang ...

  7. npm 淘宝镜像与官方源 切换

    1.临时使用 npm --registry https://registry.npm.taobao.org install 包名 2.永久设置为淘宝镜像 npm config set registry ...

  8. 分组密码(三)DES 算法— 密码学复习(六)

    在介绍完Feistel结构之后,接下来进入到著名的DES算法. 6.1 DES算法的意义 在正式介绍DES之前,首先介绍几个重要的历史时间节点. ① 1973年,美国国家标准局(NBS)向社会公开征集 ...

  9. 概述 .NET 6 ThreadPool 实现

    目录 前言 任务的调度 基本调度单元 IThreadPoolWorkItem 实现类的实例. Task 全局队列 本地队列 偷窃机制 Worker Thread 的生命周期管理 线程生命注入实验 .N ...

  10. ecshop调用指定栏目下的商品的方法

    第一步 在系统目录文件找到includes/lib_goods.php  这个文件打开 在此页最底部加入以下函数代码 /** * 首页获取指定分类产品 * * @access public * @pa ...