bzoj 1064 noi2008 假面舞会题解
Posted zhangleo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 1064 noi2008 假面舞会题解相关的知识,希望对你有一定的参考价值。
莫名其妙的变成了我们的noip互测题...
其实这题思想还是比较简单的,只是分类不好分而已
其实就是一个dfs的事
首先,非常明显,原题目中的所有关系可以抽象成一个图(这是...显而易见的吧...)
接下来,我们仅需在图上讨论即可
当然,这个图有几个部分组成其实并没有那么重要,毕竟,这些部分基本是互不干扰的。
所以接下来我们只需要对每一个块分别处理即可
我们来分类:
首先,如果所有块都是树,我们只需求出每个树上的最长链即可
接下来,如果存在环(包括真实的环和类环,即1-2-3-1和1-2-4+1-3-4两种),那么种类数最多显然是所有
环大小的gcd(至于其他的树,可以完全不必考虑了)
于是问题就变成了怎么求环的大小
请大家注意一点,就是我们所说的环的大小是指的一个环中之多可以有几种面具
也就是说,对于一个这样的环:1-2-4和1-3-4,很显然2和3的编号应该是一样的,这样我们说这个类环的大小是3!
接下来我们讨论一下怎么求
其实求法很简单:化有向图为带权无向图!
即:如果这条边是正向的,我们把他的边权设为+1,反之设为-1
这样做的目的在于,还是以上面的图为例:1-2-4和1-3-4,我们双向建边就能求出环的大小(自己画一下,一下就出来)
剩下的部分就聊尽人事了
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> using namespace std; struct Edge { int next; int to; int val; }edge[2000005]; int head[100005]; int dep[100005]; int cnt=1; int d; int n,m; int maxdep,mindep=0x3f3f3f3f; bool used[100005]; void init() { memset(head,-1,sizeof(head)); cnt=1; } int gcd(int x,int y) { if(y==0) { return x; } return gcd(y,x%y); } void add(int l,int r,int w) { edge[cnt].next=head[l]; edge[cnt].to=r; edge[cnt].val=w; head[l]=cnt++; } void dfs(int x,int deep) { used[x]=1; dep[x]=deep; maxdep=max(maxdep,dep[x]); mindep=min(mindep,dep[x]); for(int i=head[x];i!=-1;i=edge[i].next) { int to=edge[i].to; if(!used[to]) { dfs(to,deep+edge[i].val); }else { d=gcd(d,abs(deep+edge[i].val-dep[to])); } } } inline int read() { int f=1,x=0;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } int main() { // freopen("party.in","r",stdin); // freopen("party.out","w",stdout); n=read(),m=read(); init(); for(int i=1;i<=m;i++) { int x=read(),y=read(); add(x,y,1); add(y,x,-1); } int ret=0; for(int i=1;i<=n;i++) { if(!used[i]) { maxdep=0; mindep=0x3f3f3f3f; dfs(i,1); ret+=maxdep-mindep+1; } } if(!d) { if(ret>=3) { printf("%d 3 ",ret); return 0; }else { printf("-1 -1 "); return 0; } }else { if(d<3) { printf("-1 -1 "); return 0; } for(int i=3;i<=d;i++) { if(d%i==0) { printf("%d %d ",d,i); return 0; } } } }
以上是关于bzoj 1064 noi2008 假面舞会题解的主要内容,如果未能解决你的问题,请参考以下文章