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. nginx《一安装》

    linux上nginx相关 wget https://nginx.org/download/nginx-1.14.1.tar.gz tar -zxvf nginx-1.14.1.tar.gz ./co ...

  2. mybaits源码分析--缓存模块(六)

    一.缓存模块 MyBatis作为一个强大的持久层框架,缓存是其必不可少的功能之一,Mybatis中的缓存分为一级缓存和二级缓存.但本质上是一样的,都是使用Cache接口实现的.缓存位于 org.apa ...

  3. linux多次登录失败锁定账户

    2021-07-22 1.配置对系统进行失败的ssh登录尝试后锁定用户帐户 # 配置登录访问的限制 vi /etc/pam.d/system-auth 或者 vi etc/pam.d/password ...

  4. 【详细、开箱即用】.NET企业微信回调配置(数据回调URL和指令回调URL验证)

    前言: 前段时间因为公司业务需求,需要将微信小程序与企业微信对接通,也就是把小程序绑定到对应的企业微信账号下,在该企业微信的用户可以将该小程序绑定到工作台中,然后可以在工作台中打开该小程序并授权.不过 ...

  5. 【死磕NIO】— NIO基础详解

    Netty 是基于Java NIO 封装的网络通讯框架,只有充分理解了 Java NIO 才能理解好Netty的底层设计.Java NIO 由三个核心组件组件: Buffer Channel Sele ...

  6. Webpack:打包项目报错(eslint: debugger)

    打包项目需要把项目中的debugger删除,否则会报错.

  7. msf宏钓鱼

    kali下载python脚本,生成rtf文件: 下载脚本:git clone https://github.com/bhdresh/CVE-2017-8759.git 生成rtf文件: python ...

  8. 从输入 URL 到展现页面的全过程

    总体分为以下几个过程 DNS解析 TCP连接 发送HTTP请求 服务器处理请求并返回HTTP报文 浏览器解析渲染页面 连接结束 DNS解析 域名到ip地址转换 TCP连接 HTTP连接是基于TCP连接 ...

  9. 记一次docker compose的低级错误

    记一次docker compose的低级错误 问题 ​ 今天在学习dockercompose的时候,启动docker compose up,结果却出现异常 Error response from da ...

  10. TP6出现错误 No input file specified.

    解决办法是打开public下的.htaccess文件, 把:RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] 改为:RewriteRule ^(.*)$ index ...