[硫化铂]treecnt

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[硫化铂]treecnt相关的知识,希望对你有一定的参考价值。

treecnt

题目概述


题解

首先我们关注一下我们的每个限制,要求某一个点集在生成树上是一个连通块,看起来很奇怪。
如果直接通过连通块去扩展什么的很容易达到指数级复杂度,但我们可以考虑将这个条件做一定的转化。
首先,如果我们对于一个大小 ∣ S ∣ |S| S的集合 S S S,显然,我们最后得在这个集合的导出子图中选择 ∣ S ∣ − 1 |S|-1 S1条边以形成一棵树,这个导出子图中最多也只会被选择 ∣ S ∣ − 1 |S|-1 S1条边,当它最多时,恰好合法。
那我们不妨给每条边赋一个点权 w w w表示它出现在了多少个导出子图中,显然,我们如果最后得到生成树的所有边的权值和为 ∑ i = 1 k max ⁡ ( 0 , ∣ S i ∣ − 1 ) \\sum_i=1^k\\max(0,|S_i|-1) i=1kmax(0,Si1),那么就是合法的,而这又肯定是整张图的权值和的最大值。

所以我们相当于要做一个最大生成树计数。
这个过程我们可以通过 K r u s k a l Kruskal Kruskal实现,当我们真正需要选择的边就是能够连接边权比它大的边形成的森林中边权相同的边形成的连通块。
只有这些边的内部是有冲突的,我们得给它们决定出一个生成树,需要保证连通块是因为我们的基尔霍夫定理的实现需要在一个连通块内建树,然后高斯消元求行列式即可。
将每个连通块的行列式乘起来就可以得到我们的答案。
而算每条边的权值我们不能直接枚举,但可以用bitset优化,看两个端点共同出现的限制的数量,并一下即可。

时间复杂度 O ( n 3 + n 2 K ω ) O\\left(n^3+\\fracn^2K\\omega\\right) O(n3+ωn2K)

源码

#include<bits/stdc++.h> 
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
#define MAXN 2005
#define MAXM 250005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
const int mo=998244353;
template<typename _T>
void read(_T &x)
	_T f=1;x=0;char s=getchar();
	while(s<'0'||s>'9')if(s=='-')f=-1;s=getchar();
	while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
	x*=f;
 
template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
void Add(int &x,int y,int p)x=add(x,y,p);
int qkpow(int a,int s,int p)int t=1;while(s)if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;return t;
int n,K,tot,fa[505],totd,sta[MAXN],stak,ip[505],ans;
bitset<2005>st[MAXN];
struct mingint u,v,w,cnt;s[MAXM],d[MAXM];
bool cmp(ming x,ming y)return x.w>y.w;
void makeSet(int x)for(int i=1;i<=x;i++)fa[i]=i;
int findSet(int x)return fa[x]==x?x:fa[x]=findSet(fa[x]);
void unionSet(int a,int b)int u=findSet(a),v=findSet(b);if(u^v)fa[u]=v;
struct matrix
	int c[505][505],m;
	matrix()memset(c,0,sizeof(c));
	int Geass()
		int res=1;
		for(int t=1;t<m;t++)
			int k=t;for(int i=t;i<m;i++)if(c[i][t])k=i;break;
			if(k^t)for(int i=t;i<m;i++)swap(c[t][i],c[k][i]);res=mo-res;
			if(!c[t][t])break;int tmp=qkpow(c[t][t],mo-2,mo);
			for(int i=t+1;i<m;i++)if(c[i][t])
				int tp=1ll*c[i][t]*tmp%mo;
				for(int j=t;j<=m;j++)Add(c[i][j],mo-1ll*tp*c[t][j]%mo,mo);
			
		
		for(int i=1;i<m;i++)
			res=1ll*res*c[i][i]%mo;
		return res;
	
	void clear()
		for(int i=1;i<=m;i++)
			for(int j=1;j<=m;j++)
				c[i][j]=0;
		m=0;
	
A;
int main()
	//freopen("treecnt.in","r",stdin);
	//freopen("treecnt.out","w",stdout);
	read(n);read(K);int summ=0;ans=1;
	for(int i=1;i<=n;i++)
		for(int j=i+1,x;j<=n;j++)
			read(x),s[++tot]=(ming)i,j,0,x;
	for(int i=1;i<=K;i++)
		int num=0;
		for(int j=1,x;j<=n;j++)
			scanf("%1d",&x),st[j][i]=x,num+=x;
		if(!num)continue;summ+=num-1;
	
	for(int i=1;i<=tot;i++)
		s[i].w=(st[s[i].u]&st[s[i].v]).count();
	sort(s+1,s+tot+1,cmp);makeSet(n);int tp=0;
	for(int i=1,j;i<=tot;i=j+1)
		j=i;while(j<tot&&s[j+1].w==s[i].w)j++;totd=0;
		for(int k=i;k<=j;k++)
			if(findSet(s[k].u)!=findSet(s[k].v))
				d[++totd]=s[k],
				d[totd].u=findSet(s[k].u),
				d[totd].v=findSet(s[k].v);
		for(int k=i;k<=j;k++)
			if(findSet(s[k].u)^findSet(s[k].v))
				unionSet(s[k].u,s[k].v),tp+=s[k].w;
		for(int k=1;k<=j;k++)d[k].w=findSet(d[k].u);
		sort(d+1,d+totd+1,cmp);
		for(int l=1,r;l<=totd;l=r+1)
			r=l;while(r<totd&&d[r+1].w==d[l].w)r++;
			for(int k=l;k<=r;k++)
				if(!ip[d[k].u])sta[++stak]=d[k].u,ip[d[k].u]=stak;
				if(!ip[d[k].v])sta[++stak]=d[k].v,ip[d[k].v]=stak;
				Add(A.c[ip[d[k].u]][ip[d[k].u]],d[k].cnt,mo);
				Add(A.c[ip[d[k].v]][ip[d[k].v]],d[k].cnt,mo);
				Add(A.c[ip[d[k].u]][ip[d[k].v]],mo-d[k].cnt,mo);
				Add(A.c[ip[d[k].v]][ip[d[k].u]],mo-d[k].cnt,mo);
			
			A.m=stak;ans=1ll*A.Geass()*ans%mo;A.clear();
			while(stak)ip[sta[stak--]]=0;
		
	
	if(tp^summ)puts("0");return 0;
	printf("%d\\n",ans);
	return 0;

谢谢!!!

以上是关于[硫化铂]treecnt的主要内容,如果未能解决你的问题,请参考以下文章

[硫化铂]传染

[硫化铂]密码

[硫化铂]启程的日子

[硫化铂]卿且去

[硫化铂]好

[硫化铂]未来