%%%神仙题。

居然是图论,我还一直以为是二分图或者啥数据结构。

直接说正解了,将数看作节点,牌看做边,从牌的正面的数想反面连边权为1的边,反面向正面连边权为0的边(注意用到成对存储的技巧,之后会非常巧妙地用到),可以发现就是要求反转几条边可以使每个点的出读小于等于1。那么每个联通图只可能是树或基环树。可以先dfs判断求出每个联通图的点数np和边数ne,若ne/2(双向边)>np,那么这个联通图不合法直接输出-1 -1即可。

 void dfs(int x,int fa)
{
vi[x]=;np++;
for(int i=f(x);i;i=n(i))
{
ne++;
if(v(i)!=fa)
if(!vi[v(i)])dfs(v(i),x);
}
}
bool pd()
{
for(int i=;i<=n*;i++)
if(!vi[i])
{
np=ne=,dfs(i,);
if(np<ne/)return ;
}
return ;
}

代码实现

接下来考虑他是树的情况:

对于确定的跟节点,翻转的边的个数就是将所有点指向父亲节点(可以动手画一下),可以用树形dp求得。但是要枚举根节点吗?其实可以用到二次扫描和换根法,设以x为根要反转的边数为f[x],那么其实f[son]可以用f[x]更新:如果x->son的边权为1,则f[son]=f[x]-1,否则f[son]=f[x]+1.这样我们就解决了树的情况。

 int f[MAXN],st,en,ned;
void dfs1(int x,int fa)
{
v[x]=;f[x]=;
for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
{
if(!v[v(i)])
{
dfs1(v(i),x);
f[x]+=f[v(i)]+w(i);
}
else st=u(i),en=v(i),ned=i;
}
}
int f2[MAXN];
vector<int> tem;
void dfs2(int x,int fa)
{
tem.push_back(f2[x]);
for(int i=f(x);i;i=n(i))
if(v(i)!=fa && i!=ned && i!=(ned^))
{
if(w(i))f2[v(i)]=f2[x]-;
else f2[v(i)]=f2[x]+;
dfs2(v(i),x);
}
}

代码实现

下面看基环树:

在dfs时找出环上的随机一条边记录,将它去掉按树处理,最后在考虑这条边的影响。那么边的成对储存就有用了,将边的编号%2就是它从u指向v的权值。

                 ned%=;
if(f2[st]+ned==f2[en]+(ned^))minn=;
else minn=;
ans+=min(f2[st]+ned,f2[en]+(ned^));

代码实现

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define MAXN 200010
#define mod 998244353
#define LL long long
#define int LL
#define ma(x) memset(x,0,sizeof(x))
using namespace std;
struct edge
{
int u,v,w,nxt;
#define u(x) ed[x].u
#define v(x) ed[x].v
#define w(x) ed[x].w
#define n(x) ed[x].nxt
}ed[MAXN*];
int first[MAXN],num_e=;
#define f(x) first[x]
int T,n; int np,ne;
bool v[MAXN],vi[MAXN];
void dfs(int x,int fa)
{
vi[x]=;np++;
for(int i=f(x);i;i=n(i))
{
ne++;
if(v(i)!=fa)
if(!vi[v(i)])dfs(v(i),x);
}
}
bool pd()
{
for(int i=;i<=n*;i++)
if(!vi[i])
{
np=ne=,dfs(i,);
if(np<ne/)return ;
}
return ;
}
int f[MAXN],st,en,ned;
void dfs1(int x,int fa)
{
v[x]=;f[x]=;
for(int i=f(x);i;i=n(i))
if(v(i)!=fa)
{
if(!v[v(i)])
{
dfs1(v(i),x);
f[x]+=f[v(i)]+w(i);
}
else st=u(i),en=v(i),ned=i;
}
}
int f2[MAXN];
vector<int> tem;
void dfs2(int x,int fa)
{
// v[x]=1;
tem.push_back(f2[x]);
for(int i=f(x);i;i=n(i))
if(v(i)!=fa && i!=ned && i!=(ned^))
{
if(w(i))f2[v(i)]=f2[x]-;
else f2[v(i)]=f2[x]+;
// if(!v[v(i)])
dfs2(v(i),x);
}
}
inline void add(int u,int v,int w);
signed main()
{
// freopen("back5.in","r",stdin);
// freopen("1.out","w",stdout); cin>>T;
while(T--)
{
ma(f);ma(f2);ma(v);ma(vi);ma(first);num_e=;tem.clear();
scanf("%lld",&n);
int a,b;
for(int i=;i<=n;i++)
{
scanf("%lld%lld",&a,&b);
add(a,b,);add(b,a,);
}
if(pd()){puts("-1 -1");continue;}
int minn=,ans=,ans2=;
for(int i=;i<=n*;i++)
if(!v[i])
{
st=en=ned=-;tem.clear();minn=;
dfs1(i,);
f2[i]=f[i];
dfs2(i,);
if(st==-)
{
sort(tem.begin(),tem.end());
for(int j=;j<tem.size();j++)
if(tem[j]==tem[])minn++;
else break;
ans+=tem[];
}
else
{
ned%=;
if(f2[st]+ned==f2[en]+(ned^))minn=;
else minn=;
ans+=min(f2[st]+ned,f2[en]+(ned^));
}
ans2=(ans2*minn)%mod;
}
printf("%lld %lld\n",ans,ans2);
}
}
inline void add(int u,int v,int w)
{
++num_e;
u(num_e)=u;
v(num_e)=v;
w(num_e)=w;
n(num_e)=f(u);
f(u)=num_e;
}

完整代码

最新文章

  1. SQL分页查询
  2. 浅谈Android Fragment嵌套使用存在的一些BUG以及解决方法
  3. 【IDEA】IDEA 如何设置编辑器字体大小
  4. args
  5. Spring自定义一个拦截器类SomeInterceptor,实现HandlerInterceptor接口及其方法的实例
  6. Pods 更新后提示Bundle资源找不到
  7. VMware vSphere 服务器虚拟化之十七 桌面虚拟化之安装View链接服务器
  8. [ACM] HDU 1227 Fast Food (经典Dp)
  9. iOS之网络数据下载和JSON解析
  10. 无后台应用 Stash Backend
  11. PHP While 循环
  12. Spring HATEOAS的简单认识
  13. React Native 获取组件(Component)在屏幕上的位置
  14. g++编译X265
  15. 基于HTTP的长轮询简单实现
  16. jQuery效果之雪花飘落
  17. 【HQL】窗口函数
  18. SQL SERVER存储过程中使用事务
  19. Js_数组操作
  20. e810. 创建弹出菜单

热门文章

  1. 严格模式下顶层箭头函数this指向的是全局对象
  2. eclipse配置mybatis xml文件自动提示
  3. angular4 路由重用策略 RouterReuseStrategy
  4. HTML:把两张图片并排(行)显示
  5. 使用帝国备份王软件提示 Parse error: syntax error, unexpected end of file
  6. git gc干了啥
  7. Python PEP8标准
  8. Haskell 学习
  9. dialog的进度条
  10. bzoj1221 软件开发