性质一:
两个合法方案中,在一个方案
是后勤组且在另一个方案
中不是的人最多有一个,同谋者同理。
证明:反证,如果有两个,那么在
中同为后勤组保证他们间有边,与他们在
中同为同谋者矛盾。
那么拿到这个性质就可以按照网上的大多数题解直接
一波找出特解然后一个人一个人的尝试切换组之类的就可以了。
但是我们初步算一下,这个题没给边数的范围,所以我们是可能
的(算一下最大边数可以得出大概
这个数字)。
但是他们过了。也许他们用了什么奇技淫巧
性质二:
所有合法方案中不存在两个人
,
的认识人数:
的认识人数:
并且
为同谋者,
为后勤组。
证明:(这个命题给出的形式都暗示这应该是个反证)如果存在,设后勤组人数为
,则有
(后勤组间都有边),
(由假设),
(a的所有边只能连向后勤组)。那么我们可以得到
,哪里有矛盾呢?
表示
也要往
连边,所以
,矛盾。
有了这个性质,我们就可以只需要点的度数,将其从大到小排序,依次加入后勤组,再简单的利用后勤组每个点的度数和=后勤组完全图中的边数+图中所有的边数,这个等式成立时即保证了这可以形成一个合法的方案(证明可以考虑这个题没有重边)。
扫一遍组合数计算度数相等的方案即可。
结合性质一和性质二:
可以得到我们第一次合法时,剩下的(可以构成度数相等的点只有一个)或者是(只能选一个),因为其他情况时可以构造出违背性质一的方案,所以可以简单写成下面这个简单的代码而省去组合数。
#include<bits/stdc++.h>
#define maxn 5005
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
char cb[1<<18],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
void read(int &res){
char ch;
for(;!isdigit(ch=getc()););
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}
int n,m,s,a[maxn],cnt[maxn],c2[maxn];
int main(){
read(n);
rep(i,1,n){
read(a[i]),cnt[a[i]]++,m+=a[i];
rep(j,1,a[i]) read(cnt[maxn-1]);
}
m>>=1;
sort(a+1,a+1+n);
per(i,n,1){
s+=a[i];cnt[a[i]]--,c2[a[i]]++;
if((n-i+1)*(n-i)/2+m==s){
printf("%d",cnt[n-i+1]+cnt[a[i]]*c2[a[i]]-(n*(n-1)/2==m)+1);
return 0;
}
}
putchar('0');
}