有\(n\)(\(1 \le n \le 400\))只鸡,接下来按顺序进行\(m\)(\(1 \le m \le 10^5\))次操做。每次操做涉及两只鸡,若是都存在则随意拿走一只;若是只有一只存在,拿走这一只;若是都不存在,什么都不作。
求最后有多少对鸡(无序)可能共同存活。
spa
我的认为单用集合的解释方法有失偏颇。
首先考虑枚举两只鸡,规定它们必定要存活,而后模拟过程。怎么看单次模拟的复杂度都不会小于\(m\),所以要第一时间舍弃这种方法。
因而要换个角度考虑。咱们看看能不能算出某一只鸡存活的条件,再枚举两只鸡,并判断它们的条件是否冲突。
假设咱们令\(a\)必须存活。
先看那些与\(a\)有关的操做:显然,另外一只鸡在该操做前必须存活。咱们虽然获得了这个结论,可是这些操做的顺序有前后影响,并很差考虑。
为了消除后效性,咱们从后往前考虑每一个事件。若是遇到与\(a\)有关的事件\((a,b)\),咱们必须令\(b\)在这个时刻前存活。这意味着下次遇到与\(a\)或\(b\)有关的事件,咱们必须令另外一者在这个时刻前存活。咱们记若是"\(a\)必须存活",当前全部必须存活的鸡组成的集合为\(S\),则形式化地讲:
初始时,\(S\)里只有\(a\)
1.若是遇到一个事件,其中一者属于\(S\),则另外一者必须在这个时刻前存活。咱们将另外一者加进\(S\)
2.若是二者都属于\(S\),则必须死一个。这马上违反了\(S\)的定义,所以\(a\)不可能存活。咱们将其归入统计答案的考虑对象
3.若是二者都不属于\(S\),因为咱们从后往前考虑,即便这二者在更早的时间与\(a\)的生死有关,但那个有关的时刻结束以后,这二者的生死并不重要。所以这个事件不须要归入考虑范围。
由此,扫彻底部事件以后,依赖存活关系能够形象为一棵内向树(上述1.发生时,从另外一者向属于\(S\)的一者连一条有向边),咱们再也不将其看作集合考虑,由于那没法解释接下来的事情。咱们称它为\(a\)的存活树。
\(a\)的存活树的每一条边都表明着一次依赖事件,每一次事件的成功与否都决定了\(a\)可否存活。事件发生的具体顺序咱们不须要知道,可是必定是按照从叶子节点向上的某个拓扑序发生的。
考虑两只鸡\(a\)和\(b\)可否存活。有了存活树的概念,却无从下手?先从简单的一面看:若是两者的存活树的点集无交,那么显然没有影响,两者能够共存。关键是若是有交,能够共存吗?
对于一个点\(x\),其在\(a\)和\(b\)的存活树中都出现。若是\(x\)在两棵树中的父亲不一样,这表明着两次不一样的事件,前后发生,却都依赖于\(x\)。则后发生的一者必然不能保证\(x\)存活,所以\(a\)和\(b\)有一个必须死。若是\(x\)在两棵树中的父亲相同,首先两者不多是两个事件,否则两者自身都不可能存活,不在考虑范围以内;既然是同一个事件,那么它们在这一步的确共存,由于它们共同进行了有益的一步。咱们会发现,两棵树中可能出现一些“共同链”,但这并不意味着两者能够共存。由于两棵树的根必定不一样,因此“共同链”的链顶必定不是根,即“共同链”的链顶必定会出现第一个状况:父亲不同,有一只鸡必须死。
由上证毕,两只鸡能共存,当且仅当存活树的点集无交集。
在实现时,不须要建树,树只是用来严格证实的,咱们只须要计算出每只存活的鸡的存活树点集合便可。
code
#include <cstdio> #include <bitset> using namespace std; const int N=405,M=10005; typedef bitset<N> bs400; int n,m; int a[M][2]; bool die[N]; bs400 b[N]; void readData(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d",&a[i][0],&a[i][1]); } void calc(){ for(int i=1;i<=n;i++){ b[i][i]=1; for(int j=m;j>=1;j--){ int u=a[j][0],v=a[j][1]; if(b[i][u]&&b[i][v]){ die[i]=true; break; } else if(b[i][u]) b[i][v]=1; else if(b[i][v]) b[i][u]=1; } } } void print(){ int ans=0; for(int i=1;i<n;i++) if(!die[i]) for(int j=i+1;j<=n;j++) if(!die[j]){ if((b[i]&b[j]).none()) ans++; } printf("%d\n",ans); } int main(){ readData(); calc(); print(); return 0; }