zoukankan      html  css  js  c++  java
  • CF1408G Clusterization Counting

    XLVI.CF1408G Clusterization Counting

    很明显,将边按照权值从小到大排序后,依次用冰茶姬合并,如果任意时刻出现了团,则这个团显然是唯一合法的可能。人脑思考可得这个团之间的关系肯定是个划分树关系(即一个大团裂成许多小团的树形关系),因此总合法团数是 \(O(n)\) 级别的,可以暴力建出树然后在上面背包即可。

    建树的过程只需要在冰茶姬中维护当前连通块是由哪些小团拼成的即可。当发现连通块成为大团了,就从大团向小团连边,并清空小团集合,扔入大团即可。

    这要求我们在合并两个冰茶姬时同时合并它们的小团集合。使用 vector 合并大概也没问题,但是直接用 listsplice 操作 \(O(1)\) 合并它不香吗?

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=998244353;
    int n,m,dsu[1510],vsz[1510],esz[1510],cnt,f[3010][1510],lim[1510];
    struct edge{
    	int x,y,z;
    	friend bool operator<(const edge&u,const edge&v){return u.z<v.z;}
    }e[2000000];
    int find(int x){return dsu[x]==x?x:dsu[x]=find(dsu[x]);}
    list<int>v[1510],u[3010];
    void ae(int x,int y){
    	x=find(x),y=find(y);
    	if(x==y){
    		esz[x]++;
    		if(esz[x]==vsz[x]*(vsz[x]-1)/2)u[++cnt].swap(v[x]),v[x].push_back(cnt);
    		return;
    	}
    	if(vsz[x]<vsz[y])swap(x,y);
    	dsu[y]=x,vsz[x]+=vsz[y],esz[x]+=esz[y]+1,v[x].splice(v[x].begin(),v[y]);
    	if(esz[x]==vsz[x]*(vsz[x]-1)/2)u[++cnt].swap(v[x]),v[x].push_back(cnt);
    }
    void dfs(int x){
    	if(x<=n){f[x][lim[x]=1]=1;return;}
    	f[x][0]=1;
    	for(auto y:u[x]){
    		dfs(y);
    //		for(int i=0;i<=lim[x];i++)printf("%d ",f[x][i]);puts("");
    //		for(int i=0;i<=lim[y];i++)printf("%d ",f[y][i]);puts("");
    		for(int i=lim[x];i>=0;f[x][i--]=0)for(int j=lim[y];j>=0;j--)(f[x][i+j]+=1ll*f[x][i]*f[y][j]%mod)%=mod;
    		lim[x]+=lim[y];
    	}
    	f[x][0]=0,(++f[x][1])%=mod;
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)for(int j=1,x;j<=n;j++){
    		scanf("%d",&x);
    		if(i<j)m++,e[m].x=i,e[m].y=j,e[m].z=x;
    	}
    	sort(e+1,e+m+1);
    	for(int i=1;i<=n;i++)dsu[i]=i,vsz[i]=1,v[i].push_back(i);
    	cnt=n;
    //	for(int i=1;i<=m;i++)printf("(%d,%d:%d)",e[i].x,e[i].y,e[i].z);
    	for(int i=1;i<=m;i++)ae(e[i].x,e[i].y);
    //	for(int i=1;i<=n;i++)printf("%d %d %d\n",dsu[i],vsz[i],esz[i]);
    	dfs(cnt);
    	for(int i=1;i<=n;i++)printf("%d ",f[cnt][i]);
    	return 0;
    } 
    

  • 相关阅读:
    Java实时读取日志文件
    Trie树的应用:查询IP地址的ISP
    vue.extend,mixins和vue.component的区别
    前端性能优化10个方面
    vue组件和插件是实现
    vue指令用法
    promise retry实现
    linux管道与重定向
    linux文件颜色与类型
    linux文件权限说明
  • 原文地址:https://www.cnblogs.com/Troverld/p/14613587.html
Copyright © 2011-2022 走看看