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;
}

最新文章

  1. 开源一个WEB版本GEF,基于SVG的网页流程图框架
  2. Linux内核配置、编译及Makefile简述
  3. jQuery的attr与prop
  4. vsftp虚拟用户配置
  5. js闭包用法
  6. MySQL注入中load_file()函数的应用
  7. 懒省事的小明--nyoj55
  8. [Unity3D]转让Android介面
  9. 《编程简介(Java) &amp;#183;10.3递归思想》
  10. Salesforce apex标签的有关内容
  11. IIS7部署MVC站点后,打开无法正常跳转到首页
  12. Luogu4149:[IOI2011]Race
  13. c#抽取pdf文档标题(4)——机器学习以及决策树
  14. windows环境安装phantomjs和pyspider遇到的问题
  15. MSSQL:查看所有触发器信息的命令
  16. linux性能采用工具oprofile使用
  17. 在Windows下制作静态库和动态库
  18. 为linux扩展swap分区
  19. 查询相应的key
  20. CocoaPods 2017最新、最快安装和使用说明

热门文章

  1. Swift-Button 的 highlighted(高亮)
  2. 遇到Web页面禁用鼠标右键操作时,该如何解禁?
  3. JVM双亲委派模型及其优点
  4. uni-app中websocket的使用 断开重连、心跳机制
  5. Vue 路由跳转报错 Error: Avoided redundant navigation to current location: &quot;/XXX&quot;.
  6. Request 根据用户输入的信息获取输入到控制台
  7. 深度探索-Redis复制
  8. 一文详解JavaScript的继承模式
  9. Jvm调优理论篇
  10. Jmeter系列(15)- 常用断言之大小断言