题解 「HDU6403」卡片游戏
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」卡片游戏的更多相关文章
- 题解 「SDOI2017」硬币游戏
题目传送门 Description 周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利. 大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了. 同学们觉得要加强 ...
- 【LOJ】#2067. 「SDOI2016」硬币游戏
题解 c一样的就是一个独立的游戏 我们对于2和3的指数 sg[i][j] 表示\(c \cdot 2^i \cdot 3^j\)的棋子,只有这个硬币是反面,翻转的硬币是正面的sg值 枚举sg函数所有可 ...
- 「Githug」Git 游戏通关流程
Githug 他喵的这是个啥!?难道不是 GitHub 拼错了么,和 Git 什么关系? 和游戏又有什么关系? 其实,他的元身在这里:https://github.com/Gazler/githug ...
- 【LOJ】#2182. 「SDOI2015」寻宝游戏
题解 终于了解怎么动态维护虚树了 就是把点按照dfs序排个序啊 这道题显然是求虚树上所有边长的两倍 我们把dfs序排完序,相邻两个点加上路径长(包括首尾),删除的时候删一个点减去它到两边再加上新近相邻 ...
- 【LOJ】#2126. 「HAOI2015」数组游戏
题解 简单分析一下就知道\(\lfloor \frac{N}{i} \rfloor\)相同的\(i\)的\(sg\)函数相同 所以我们只要算\(\sqrt{n}\)个\(sg\)函数就好 算每一个\( ...
- 【LOJ】#2562. 「SDOI2018」战略游戏
题解 圆方树建好之后点是原来的两倍,而st表求lca也要开到点的两倍,所以是四倍 我并没有开小,然而= =,我的预处理log2,写成了200000,而不是400000 我是不是折翼啊= = 很可写,我 ...
- LG1640 「SCOI2010」连续攻击游戏 二分图最大匹配
问题描述 LG1640 题解 一开始以为是把\((a,b)\)作为左右部点,发现\(n \le 1000000\),建图是\(O(n^2)\)的,会爆掉 属性值向\(i\)建边. \(\mathrm{ ...
- @loj - 2004@ 「SDOI2017」硬币游戏
目录 @description@ @solution@ @accepted code@ @details@ @description@ 周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数 ...
- 【LOJ 2004】「SDOI2017」硬币游戏
LOJ 2004 100pts 首先我们肯定要建AC自动机的.. 那么这题就肯定是个AC自动机上\(dp\). 所以想想状态. 首先如果我们把状态设成这样行不行: \(dp(i)\)表示匹配到了i节点 ...
随机推荐
- node十年心酸史,带你了解大前端的由来!
前言 近年来,随着前端的丰富,前后端分离是趋势.各种东西如雨后春笋一般,层出不穷.node.js的出现,使前端真正意义上变成了大前端. 前端由来之HTML发展史 1990 年,Tim Berners- ...
- java js转码解码
摘自网友:https://blog.csdn.net/sgear/article/details/1509400?utm_medium=distribute.pc_relevant.none-task ...
- cmd(命令行)超好用的技巧,很不错的打开方式
超快速打开管理cmd widows + x 按a 直接打开文件位置,在地址栏输入cmd 地址----直接cmd打开到所在文件位置 ex:cmd D:\work cd ../../../ 返回上几层的方 ...
- Mybatis-Plus增强包
简介 本框架(Gitee地址 )结合公司日常业务场景,对Mybatis-Plus 做了进一步的拓展封装,即保留MP原功能,又添加更多有用便捷的功能.具体拓展体现在数据自动填充(类似JPA中的审计).关 ...
- Defence
emm...这道题我调了一下午你敢信?? 好吧还是我太天真了. 开始的时候以为自己线段树动态开点与合并写错了,就调; 结果发现没问题,那就是信息维护错了. 一开始以为自己最左右的1 ...
- 简说yuv
最近弄了一个读取y4m文件转成yuv的流的事情,记录一些yuv相关的细节 为什么会有yuv 因为我们目前的显示器显示的原理都是三原色,几乎所有的视频数据最后都要转为rgb格式才能渲染到显示屏上,而原始 ...
- Identity角色管理四(删除角色)
角色删除方法 [HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> Delete(string id) ...
- FastAPI(2)- 快速入门
安装 FastAPI pip install fastapi # 将来需要将应用程序部署到生产环境可以安装 uvicorn 作为服务器 pip install uvicorn 最简单的代码栗子 fro ...
- MySQL MHA高可用集群部署及故障切换
一.MHA概念MHA(MasterHigh Availability)是一套优秀的MySQL高可用环境下故障切换和主从复制的软件.MHA 的出现就是解决MySQL 单点的问题.MySQL故障切换过程中 ...
- pythonGUI-PySide2的使用笔记
用python开发跨平台的图形化界面,主流的有3种选择: Tkinter 基于Tk的Python库,Python官方标准库,稳定.发布程序较小,缺点是控件相对较少. wxPython 基于wxWidg ...