您的位置:首页 > 其它

【bzoj3444】最后的晚餐 乱搞

2016-03-11 08:32 169 查看
不在一个联通块里的肯定不影响,最后组合数算一下就可以

问题在于同一个联通块里有多少种排法

这道题表面上是有向边,实际上是无向边,因为要强制坐在一起

一个点的度数最多为2,且如果出现长度大于2的环则无解

那么就是说没有环喽,也就是每个点都在一条链上

现在问题就是一条链有多少种排法,这个问题很明显吧,就是等于2呀

ans=(cnt+cnt')!*(2^cnt)

cnt表示链的数量,cnt'表示单个点的个数

怎么求有多少条链呢?并查集呗

先计算一下每个节点的度是否大于2,dfs判断一下有没有环

重边的问题比较蛋疼

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#define maxn 500100
#define mod 989381

using namespace std;

int head[maxn],to[2*maxn],next[2*maxn],f[maxn];
bool vis[maxn],flag[maxn],tag[maxn];
int n,m,num;

void addedge(int x,int y)
{
num++;to[num]=y;next[num]=head[x];head[x]=num;
}

bool dfs(int x,int fa)
{
vis[x]=1;
for (int p=head[x];p;p=next[p])
if (to[p]!=fa && !tag[to[p]])
{
if (vis[to[p]]) return 1;
if (dfs(to[p],x)) return 1;
tag[to[p]]=1;
}
for (int p=head[x];p;p=next[p]) tag[to[p]]=0;
return 0;
}

int power(int x,int y)
{
int ans=1;
while (y)
{
if (y&1) ans=(long long)ans*x%mod;
x=(long long)x*x%mod;
y>>=1;
}
return ans;
}

int find(int x)
{
if (f[x]==x) return x;
else return f[x]=find(f[x]);
}

int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) f[i]=i;
for (int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
int f1=find(x),f2=find(y);
if (f1!=f2) f[f1]=f2;
addedge(x,y);addedge(y,x);
flag[x]=flag[y]=1;
}
for (int i=1;i<=n;i++)
{
int cnt=0;
for (int p=head[i];p;p=next[p])
if (!vis[to[p]])
{
vis[to[p]]=1;cnt++;
if (cnt>2) {printf("0\n");return 0;}
}
for (int p=head[i];p;p=next[p]) vis[to[p]]=0;
}
memset(vis,0,sizeof(vis));
for (int i=1;i<=n;i++)
if (!vis[i])
if (dfs(i,0)) {printf("0\n",i);return 0;}
int ans=1,cnt=0,cnt1=0;
for (int i=1;i<=n;i++) if (f[i]==i) cnt++;
for (int i=1;i<=n;i++) if (!flag[i]) cnt1++;
for (int i=1;i<=cnt;i++) ans=(long long)ans*i%mod;
ans=(long long)ans*power(2,cnt-cnt1)%mod;
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: