题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1016

就是缩点,每次相同权值的边构成的联通块求一下matrix tree。注意gauss里的编号应该是从1到...的连续的。

学习了一个TJ。用了vector。自己曾写过一个只能过样例的。都放上来吧。

路径压缩的话会慢?循环里ed[i].w!=ed[i+1].w的话会慢?

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=,M=,mod=;
int n,m,fa[N],pa[N],c[N][N],a[N][N],ans=;//ans=1
bool vis[N];
vector<int> v[N];
struct Ed{
int x,y,w;
bool operator< (const Ed &b)const
{return w<b.w;}
}ed[M];
int find(int a,int f[]){return f[a]==a?a:find(f[a],f);} //加上路径压缩会变慢!!!
int gauss(int n)
{
bool fx=;int ret=;
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)a[i][j]%=mod;//
for(int i=;i<=n;i++)
{
int k=i;
for(int j=i+;j<=n;j++)if(abs(a[j][i])>abs(a[k][i]))k=j;
if(k!=i)
{
fx=!fx;for(int j=i;j<=n;j++)swap(a[i][j],a[k][j]);
}
for(int j=i+;j<=n;j++)
while(a[j][i])
{
fx=!fx;int tmp=a[i][i]/a[j][i];
for(int l=i;l<=n;l++)
{
int tp=a[i][l];a[i][l]=a[j][l];//i=j
a[j][l]=(tp-tmp*a[j][l])%mod;//j=i%j
}
}
(ret*=a[i][i])%=mod;
}
return (ret*(fx?-:)+mod)%mod;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)fa[i]=i,pa[i]=i;
for(int i=;i<=m;i++)
scanf("%d%d%d",&ed[i].x,&ed[i].y,&ed[i].w);
sort(ed+,ed+m+);
for(int i=;i<=m+;i++)//
{ if(ed[i-].w!=ed[i].w||i==m+)//
{
for(int j=;j<=n;j++)
{
if(!vis[j])continue;
v[find(j,pa)].push_back(j);
vis[j]=;
}
for(int j=;j<=n;j++)
{
if(v[j].size()<=)continue;
memset(a,,sizeof a);//!
int siz=v[j].size();
for(int l0=;l0<siz;l0++)
for(int l1=l0+;l1<siz;l1++)
{
int x=v[j][l0],y=v[j][l1],t=c[x][y];//x=v[j][l0],don't use l0 directly
a[l0+][l0+]+=t;a[l1+][l1+]+=t;
a[l0+][l1+]-=t;a[l1+][l0+]-=t;//but here is l0/1, for in gauss the bh must from 1 and be continous
}
(ans*=gauss(siz-))%=mod;
for(int k=;k<siz;k++)fa[v[j][k]]=j;//fa -> pa
}
for(int j=;j<=n;j++)
{
fa[j]=pa[j]=find(j,fa);v[j].clear();
}
}
int f1=find(ed[i].x,fa),f2=find(ed[i].y,fa);
if(f1==f2)continue;vis[f1]=;vis[f2]=;
pa[find(f1,pa)]=find(f2,pa);c[f1][f2]++;c[f2][f1]++;
}
for(int i=;i<n;i++)if(find(i,fa)!=find(i+,fa)){printf("");return ;}
printf("%d",ans);
return ;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=,M=,mod=;
int n,m,fa[N],pa[N],c[N][N],a[N][N],ans=;//ans=1
bool vis[N];
vector<int> v[N];
struct Ed{
int x,y,w;
bool operator< (const Ed &b)const
{return w<b.w;}
}ed[M];
int find(int a,int f[]){return f[a]==a?a:find(f[a],f);} //加上路径压缩会变慢!!!
int gauss(int n)
{
bool fx=;int ret=;
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)a[i][j]%=mod;//
for(int i=;i<=n;i++)
{
int k=i;
for(int j=i+;j<=n;j++)if(abs(a[j][i])>abs(a[k][i]))k=j;
if(k!=i)
{
fx=!fx;for(int j=i;j<=n;j++)swap(a[i][j],a[k][j]);
}
for(int j=i+;j<=n;j++)
while(a[j][i])
{
fx=!fx;int tmp=a[i][i]/a[j][i];
for(int l=i;l<=n;l++)
{
int tp=a[i][l];a[i][l]=a[j][l];//i=j
a[j][l]=(tp-tmp*a[j][l])%mod;//j=i%j
}
}
(ret*=a[i][i])%=mod;
}
return (ret*(fx?-:)+mod)%mod;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)fa[i]=i,pa[i]=i;
for(int i=;i<=m;i++)
scanf("%d%d%d",&ed[i].x,&ed[i].y,&ed[i].w);
sort(ed+,ed+m+);
for(int i=;i<=m;i++)
{
int f1=find(ed[i].x,fa),f2=find(ed[i].y,fa);
if(f1!=f2){
vis[f1]=;vis[f2]=;
pa[find(f1,pa)]=find(f2,pa);c[f1][f2]++;c[f2][f1]++;
}
// if(f1==f2)continue;//!!!!!!!
if(ed[i+].w!=ed[i].w||i==m)
{
for(int j=;j<=n;j++)
{
if(!vis[j])continue;
v[find(j,pa)].push_back(j);
vis[j]=;
}
for(int j=;j<=n;j++)
{
if(v[j].size()<=)continue;
memset(a,,sizeof a);//!
int siz=v[j].size();
for(int l0=;l0<siz;l0++)
for(int l1=l0+;l1<siz;l1++)
{
int x=v[j][l0],y=v[j][l1],t=c[x][y];//x=v[j][l0],don't use l0 directly
a[l0+][l0+]+=t;a[l1+][l1+]+=t;
a[l0+][l1+]-=t;a[l1+][l0+]-=t;//but here is l0/1, for in gauss the bh must from 1 and be continous
}
(ans*=gauss(siz-))%=mod;
for(int k=;k<siz;k++)fa[v[j][k]]=j;//fa -> pa
}
for(int j=;j<=n;j++)
{
fa[j]=pa[j]=find(j,fa);v[j].clear();
}
}
}
for(int i=;i<n;i++)if(find(i,fa)!=find(i+,fa)){printf("");return ;}
printf("%d",ans);
return ;
}

改一下循环(慢?)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=,M=,mod=;
int n,m,p0,p1,col[N],cnt,head[N],xnt,fnw,ans=,fa[N],bh[N],tot;
bool used[M<<],vis[N],nd[N],tvis[N];
struct Ed{
int next,x,y,w;
Ed(int x=,int y=,int w=):x(x),y(y),w(w) {}
bool operator< (const Ed &b)const
{return w<b.w;}
}ed[M<<];
struct Matrix{
int a[N][N];
void init(){memset(a,,sizeof a);}
Matrix operator- (const Matrix &b)const
{
Matrix c;c.init();
for(int i=;i<tot;i++)
for(int j=;j<tot;j++)
c.a[i][j]=(a[i][j]-b.a[i][j])%mod;//
return c;
}
}d,l,r;
int find(int a){return fa[a]==a?a:fa[a]=find(fa[a]);}
void add(int x,int y,int w)
{
ed[++xnt]=Ed(x,y,w);
ed[++xnt]=Ed(y,x,w);
if(find(x)!=find(y))fa[find(x)]=find(y);
}
void dfst(int cr)
{
tvis[cr]=;bh[cr]=++tot;
for(int i=head[cr],v;i;i=ed[i].next)
if(used[i]&&!tvis[ed[i].y])dfst(ed[i].y);
}
void dfsx(int cr,int f) //dfs处理好这一块的度数矩阵和邻接矩阵
{
vis[cr]=;
for(int i=head[cr],v;i;i=ed[i].next)
if(used[i]&&(v=ed[i].y)!=f)
{
d.a[bh[cr]][bh[cr]]++;if(!vis[v])d.a[bh[v]][bh[v]]++;
l.a[bh[cr]][bh[v]]=l.a[bh[v]][bh[cr]]=;
if(!vis[v])dfsx(v,cr);
}
}
void gauss()
{
int fx=,ret=;
for(int i=;i<tot;i++)//<tot,少一行一列
{
int nw=i;
for(int j=i+;j<tot;j++)if(abs(r.a[j][i])>abs(r.a[nw][i]))nw=j;
if(nw!=i)
{
fx=-fx;for(int l=i;l<tot;l++)swap(r.a[i][l],r.a[nw][l]);
}
for(int j=i+;j<tot;j++)
while(r.a[j][i])
{
int tmp=r.a[i][i]/r.a[j][i];
for(int l=i;l<tot;l++)
{
int tp=r.a[i][l];r.a[i][l]=r.a[j][l];//i=j;
r.a[j][l]=(tp-r.a[j][l]*tmp)%mod;//j=i%j
}
fx=-fx;
}
(ret*=r.a[i][i])%=mod; //不要在上面赋值:消下面的时候可能换过来!
}
ret=(ret*fx+mod)%mod;
(fnw*=ret)%=mod;
}
void cal(int x) //当前权值的一个个联通块
{
d.init();l.init();
memcpy(tvis,vis,sizeof vis);tot=;//bh!
dfst(x);dfsx(x,);
r=d-l;
gauss();
}
void dfs(int cr)
{
col[cr]=cnt;
for(int i=head[cr];i;i=ed[i].next)
if(used[i]&&!col[ed[i].y])
dfs(ed[i].y);
}
void solve() //一层
{
memset(nd,,sizeof nd);memset(used,,sizeof used);
for(int i=p0;i<=p1;i++)
if(ed[i].x!=ed[i].y) //边的两边连的是已经缩点后的
used[i]=,nd[ed[i].x]=,nd[ed[i].y]=; //能用的边和涉及的点
memset(vis,,sizeof vis);memset(bh,,sizeof bh);fnw=;
for(int i=;i<=cnt;i++)if(!vis[i]&&nd[i])cal(i); //每个联通块
(ans*=fnw)%=mod;
cnt=;memset(col,,sizeof col); //cnt:现在有多少点(缩点后)
for(int i=;i<=cnt;i++)if(!col[i])cnt++,dfs(i); //缩点
for(int i=p1+;i<=xnt;i++)ed[i].x=col[ed[i].x],ed[i].y=col[ed[i].y];
}
int main()
{
scanf("%d%d",&n,&m);int x,y,w;
for(int i=;i<=n;i++)fa[i]=i;
for(int i=;i<=m;i++)scanf("%d%d%d",&x,&y,&w),add(x,y,w);
for(int i=;i<=n;i++)if(find(i-)!=find(i)){printf("");return ;}//
sort(ed+,ed+xnt+);cnt=n;
for(int i=;i<=xnt;i++)
{
ed[i].next=head[ed[i].x];
head[ed[i].x]=i;
}
for(int i=;i<=xnt;i++)
{
p0=i;while(ed[i+].w==ed[i].w)i++;p1=i;
solve();
}
printf("%d",ans);
return ;
}

只能过样例的

bzoj 1016 [JSOI2008]最小生成树计数——matrix tree(相同权值的边为阶段缩点)(码力)的更多相关文章

  1. BZOJ.1016.[JSOI2008]最小生成树计数(Matrix Tree定理 Kruskal)

    题目链接 最小生成树有两个性质: 1.在不同的MST中某种权值的边出现的次数是一定的. 2.在不同的MST中,连接完某种权值的边后,形成的连通块的状态是一样的. \(Solution1\) 由这两个性 ...

  2. BZOJ 1016: [JSOI2008]最小生成树计数( kruskal + dfs )

    不同最小生成树中权值相同的边数量是一定的, 而且他们对连通性的贡献是一样的.对权值相同的边放在一起(至多10), 暴搜他们有多少种方案, 然后乘法原理. ----------------------- ...

  3. [BZOJ 1016] [JSOI2008] 最小生成树计数 【DFS】

    题目链接:BZOJ - 1016 题目分析 最小生成树的两个性质: 同一个图的最小生成树,满足: 1)同一种权值的边的个数相等 2)用Kruscal按照从小到大,处理完某一种权值的所有边后,图的连通性 ...

  4. [BZOJ]1016 JSOI2008 最小生成树计数

    最小生成树计数 题目描述 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同 ...

  5. bzoj 1016: [JSOI2008]最小生成树计数【dfs+克鲁斯卡尔】

    有一个性质就是组成最小生成树总边权值的若干边权总是相等的 这意味着按边权排序后在权值相同的一段区间内的边能被选入最小生成树的条数是固定的 所以先随便求一个最小生成树,把每段的入选边数记录下来 然后对于 ...

  6. BZOJ 1016 [JSOI2008]最小生成树计数 ——Matrix-Tree定理

    考虑从小往大加边,然后把所有联通块的生成树个数计算出来. 然后把他们缩成一个点,继续添加下一组. 最后乘法原理即可. 写起来很恶心 #include <queue> #include &l ...

  7. 【BZOJ 1016】 1016: [JSOI2008]最小生成树计数 (DFS|矩阵树定理)

    1016: [JSOI2008]最小生成树计数 Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树 ...

  8. 1016: [JSOI2008]最小生成树计数

    1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 6200  Solved: 2518[Submit][St ...

  9. 1016: [JSOI2008]最小生成树计数 - BZOJ

    Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的 ...

随机推荐

  1. 【HackerRank】Manasa and Stones

    Change language : Manasa 和 她的朋友出去徒步旅行.她发现一条小河里边顺序排列着带有数值的石头.她开始沿河而走,发现相邻两个石头上的数值增加 a 或者 b. 这条小河的尽头有一 ...

  2. R中的参数传递函数:commandArgs(),getopt().

    1.commandArgs(),是R自带的参数传递函数,属于位置参数. ##test.R args=commandArgs(T) print (args[1])##第一个外部参数 print (arg ...

  3. ==、equals与hashCode

    ==  首先,得说明java数据类型分为基本数据类型和引用数据类型, 基本数据类型有8种: 浮点型:float(4 byte), double(8 byte) 整型:byte(1 byte), sho ...

  4. Response对象介绍(服务器到客户端)

    1.response的状态码和响应头设置 package com.test; import java.io.IOException; import java.io.PrintWriter; impor ...

  5. Mysql -- You can't specify target table 'address' for update in FROM clause

    做地址管理时,需要先根据要设为默认的地址的用户将用户的其他地址都设置为非默认 需要select出用户id然后update 原语句 update address set isdeafult = 0 wh ...

  6. winform获取文件路径

    1.取得控制台应用程序的根目录方法     方法1.Environment.CurrentDirectory 取得或设置当前工作目录的完整限定路径     方法2.AppDomain.CurrentD ...

  7. HIVE 配置文件详解

    hive的配置: hive.ddl.output.format:hive的ddl语句的输出格式,默认是text,纯文本,还有json格式,这个是0.90以后才出的新配置: hive.exec.scri ...

  8. Java正则表达中Greedy Reluctant Possessive 的区别

    Java正则表达中Greedy Reluctant Possessive 的区别 分类: java2015-01-16 00:28 1280人阅读 评论(9) 收藏 举报 正则表达式Java   目录 ...

  9. JSON解析字符串

    JSON解析字符串 JSON 解析字符串时,应按严格的标准,否则无法解析: str1 = '{"str":"string","number" ...

  10. SQL SERVER 日志已满的处理方法 (转)

    事务日志文件Transaction Log File是用来记录数据库更新情况的文件,扩展名为ldf.在 SQL Server 7.0 和 SQL Server 2000 中,如果设置了自动增长功能,事 ...