洛谷题目传送门
题目描述
There are n computers in the company network. They are numbered from 1 to n .
For each pair of two computers 1≤i<j≤n you know the value ai,j : the difficulty of sending data between computers i and j . All values ai,j for i<j are different.
You want to separate all computers into kk sets A1,A2,…,Ak , such that the following conditions are satisfied:
- for each computer 1≤i≤n there is exactly one set Aj , such that i∈Aj ;
- for each two pairs of computers (s,f) and (x,y) (s=f , x=y ), such that s , f , x are from the same set but x and y are from different sets, as,f<ax,y .
For each 1≤k≤n find the number of ways to divide computers into k groups, such that all required conditions are satisfied. These values can be large, so you need to find them by modulo 998244353 .
输入格式
The first line contains a single integer n ( 1≤n≤1500 ): the number of computers.
The i-th of the next n lines contains n integers ai,1,ai,2,…,ai,n ( 0≤ai,j≤2n(n−1) ).
It is guaranteed that:
- for all 1≤i≤n ai,i=0 ;
- for all 1≤i<j≤n ai,j>0 ;
- for all 1≤i<j≤n ai,j=aj,i ;
- all ai,j for i<j are different.
输出格式
Print n integers: the k -th of them should be equal to the number of possible ways to divide computers into k groups, such that all required conditions are satisfied, modulo 998244353 .
题意翻译
- 给定 n 个点的带权无向完全图,点 i,j 之间的权值为 ai,j,权值是一个 1∼2n(n−1) 的排列。
- 计数把原图划分成 k 个组的方案数,满足:
- 对于任意的 (s, f), (x, y),其中 s,f,x 同组,y 与 x 不同组 (s=f,x=y),as,f<ax,y,即(对于每个组)组间边大于组内边。
- 输出一行 n 个数,对于 k∈[1,n] 求出答案,对 998244353 取模。
- 1≤n≤1500
输入输出样例
输入 #1
4
0 3 4 6
3 0 2 1
4 2 0 5
6 1 5 0
输出 #1
1 0 1 1
输入 #2
7
0 1 18 15 19 12 21
1 0 16 13 17 20 14
18 16 0 2 7 10 9
15 13 2 0 6 8 11
19 17 7 6 0 4 5
12 20 10 8 4 0 3
21 14 9 11 5 3 0
输出 #2
1 1 2 3 4 3 1
说明/提示
Here are all possible ways to separate all computers into 4 groups in the second example:
- {1,2},{3,4},{5},{6,7} ;
- {1},{2},{3,4},{5,6,7} ;
- {1,2},{3},{4},{5,6,7} .
题意简介
给定一个带权无向完全图,要求把它划分成 k 个组,并对任意一个组来说,这个组与另一个组的任意一条连边都大于任何一条组之间的边,求方案数
解题思路
由题意可知每个组都是一个完全图,因此一个组符合条件,当且仅当他所有的边加完之后(也就是成为一个完全图之后),组间边 > 组内边
看到这种有边权限制的联通块,不难想到 Kruskal 重构树
按从小到大把边排序之后建出 Kruskal 重构树,然后可以得出
对于重构树上的任意两个点来说,只要这两个点的子树没有重合部分,那么这两个点的子树上的叶子结点所组成的组一定是符合条件的。
因此,问题就变成了在重构树上选k个节点,他们的子树不相交,且他们能覆盖所有叶子结点的方案数。
这就是一个DP问题了
首先遍历一遍重构树,把叶节点变成一个序列,然后把重构树上的节点所对应的区间的 L 和 R存起来,具体来说可以在每个 R 上建立一个vector,然后把所有以这点为R的区间的L存进去
(认真理解一下)
设 f[i][j]表示在序列上的第 i 个点,已经分成 j 个区间的方案数
那么 枚举一下最后分的一个区间的右端点,则
f[i][j]=f[i][j]+f[L[k]][j-1](L[k],为右端点为i的重构树区间的左端点)
代码如下
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL N =3e3+700;
const LL mod = 998244353;
struct node
{
LL x,y,v;
}e[N*N];
LL a[1500][1500],t,L[N],R[N],n,m,fa[N],flag[N],siz[N],cntE[N];
LL f[1500][1500];
LL cnt=0,tot=0;
LL lson[N],rson[N];
vector<LL> Set[N];
void add(LL x,LL y,LL v)
{
e[++t].x=x;
e[t].y=y;
e[t].v=v;
}
LL Find(LL x)
{
if(fa[x]==x) return x;
return fa[x]=Find(fa[x]);
}
bool cmp(node x,node y)
{
return x.v<y.v;
}
void pre_work()
{
for(LL i=1;i<=n;i++)
{
flag[i]=0; //flag表示以i为根节点的子树是不是已经构成完全图
fa[i]=i;
siz[i]=1;
cntE[i]=0; //cntE是以i为根的子树的边数和
Set[i].push_back(i); //叶节点的左端点也是自己
}
}
void calc(LL x)
{
++cntE[x];
if(cntE[x]==(siz[x]*(siz[x]-1)/2))
flag[x]=1;
}
void Mark(LL x) //计算左右端点
{
if(x<=n)
{
++tot;
L[x]=tot;
R[x]=tot;
return;
}
L[x]=n+1;
Mark(lson[x]);
L[x]=min(L[x],L[lson[x]]);
Mark(rson[x]);
L[x]=min(L[x],L[lson[x]]);
R[x]=tot;
if(flag[x])
{
Set[R[x]].push_back(L[x]);
}
}
inline LL read()
{
LL ans=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9'){ans=(ans<<1)+(ans<<3)+(ch^48); ch=getchar();}
return ans*f;
}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
n=read();
m=n*n;
for(LL i=1;i<=n;i++)
{
for(LL j=1;j<=n;j++)
{
LL v=read();
a[i][j]=v;
if(i<j)
add(i,j,v);
}
}
sort(e+1,e+t+1,cmp);
cnt=n;
pre_work();
for(LL i=1;i<=t;i++)
{
LL x=e[i].x,y=e[i].y;
LL fx=Find(x),fy=Find(y);
if(fx!=fy)
{
++cnt;
lson[cnt]=fx;
rson[cnt]=fy;
fa[fx]=cnt;
fa[fy]=cnt;
fa[cnt]=cnt;
siz[cnt]=siz[fx]+siz[fy];
cntE[cnt]=cntE[fx]+cntE[fy];
calc(cnt);
}
else calc(fx);
}
Mark(cnt);
f[0][0]=1;
for(LL i=1;i<=n;i++)
{
for(LL k=1;k<=n;k++)
{
for(LL v=0;v<Set[i].size();v++)
{
LL j=Set[i][v];
f[i][k]=(f[i][k]+f[j-1][k-1])%mod;
}
}
}
for(LL i=1;i<=n;i++)
printf("%lld ",f[n][i]);
return 0;
}