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. 腾讯云 TKE Everywhere 特性发布,用户可在自有基础设施中托管 K8s 服务

    作者 孔令飞,腾讯云资深工程师,拥有大规模 Kubernetes 集群.微服务的研发和架构经验,目前专注于云原生混合云领域的基础架构开发. 朱翔,腾讯云容器服务高级产品经理,目前负责云原生混合云产品方 ...

  2. jdbc操作mysql(四):利用反射封装

    前言 有了前面利用注解拼接sql语句,下面来看一下利用反射获取类的属性和方法 不过好像有一个问题,数据库中的表名和字段中带有下划线该如何解决呢 实践操作 工具类:获取connection对象 publ ...

  3. VS Code 1.60 发布!竟然可以自动检测编程语言了!

    北京时间 2021 年 9 月 3 日凌晨,微软正式发布 2021 年 8 月版的 Visual Studio Code.希望您会喜欢此版本中的许多更新与改进,以下是其中的一些亮点: * 自动语言检测 ...

  4. 洛谷P2338 Bessie Slows Down S 题解

    题目 [USACO14JAN]Bessie Slows Down S 题解 这道题其实蛮简单的,不知道为什么难度划到了提高+,个人觉得这难度大概就是普及左右. 具体说说怎么做吧,简单模拟一下即可,始终 ...

  5. Intel® QAT 加速卡之数据面流程(图)

    QAT数据面流程 sessionSetupData数据结构 pOpData数据结构

  6. vim的配置文件

    网上一个比较常见的配置文件设置如下,这个配置还是很棒的,尤其创建脚本或者c文件时 " All system-wide defaults are set in $VIMRUNTIME/debi ...

  7. fwm环境APP菜品数据加载失败的优化操作

    1)在项目的.env文件中添加如下一行: RESPONSE_CACHE_ENABLED=true 2)拷贝 laravel-worker.conf.example,将laravel字段替换为域名,并执 ...

  8. [源码解析] 深度学习流水线并行 PipeDream(5)--- 通信模块

    [源码解析] 深度学习流水线并行 PipeDream(5)--- 通信模块 目录 [源码解析] 深度学习流水线并行 PipeDream(5)--- 通信模块 0x00 摘要 0x01 前言 0x02 ...

  9. 判断input radio选中那个

    var _sex=$("input[name='sex']:checked").val(); if(_sex==null){ layer.msg("请选择性别" ...

  10. vuex前端工程化之动态导入文件--require.context( )

    随着项目的复杂,文件结构越来越多,Store中modules中的文件也越来越多,如果一个一个加载是不是很麻烦呢? 先看一个项目中store项目结构: 过去都是通过import分别引入文件: 1 imp ...