链接
https://nanti.jisuanke.com/t/40371
题解
首先进行拓扑排序构造出原序列
独立集:点集内部没有连边,对应选出的序列是递增的
支配集:每个外部的点都和选出的点集中的某个点构成逆序,那也就是说如果我把它加进来,它也和原来集合中的点不构成逆序,这样的点必须全加进来
那其实就是求极长上升子序列的个数(即这个子序列中不能再加入别的数字使之还是上升子序列)
极小支配集和极大独立集的关系
实际上,独立集和支配集本身就有天然的联系,每个极小支配集都是极大独立集
要让一个集合同时是支配集和独立集,那它肯定是极小支配集、极大独立集
因为在极大独立集中任意加入一个元素,这个集合都会产生内部连边,所以外部的所有点都和极大独立集中的点有连边,所以极大独立集是支配集
在极大独立集中任意删去一个点,这个点就不能被支配,所以极大独立集是极小支配集
(思路取自西北工业大学《离散数学》教材)
代码
#include <bits/stdc++.h>
#define eps 1e-8
#define iinf 0x3f3f3f3f
#define linf (1ll<<60)
#define cl(x) memset(x,0,sizeof(x))
#define mod 1000000007ll
#define maxn 1010
#define maxe 100010
using namespace std;
typedef long long ll;
ll read(int x=0)
{
int c, f=1;
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
for(;isdigit(c);c=getchar())x=x*10+c-48;
return f*x;
}
struct Graph
{
int etot, head[maxn], to[maxe], next[maxe], w[maxe];
void clear(int N)
{
int i;
for(i=1;i<=N;i++)head[i]=0;
for(i=1;i<=etot;i++)to[i]=next[i]=w[i]=0;
etot=0;
}
void adde(int a, int b, int c){to[++etot]=b;w[etot]=c;next[etot]=head[a];head[a]=etot;}
}G;
ll N, M, f[maxn], a[maxn], indeg[maxn], tim;
void BFS()
{
priority_queue<ll,vector<ll>,greater<ll>> q;
for(ll i=1;i<=N;i++)if(indeg[i]==0)q.emplace(i);
while(!q.empty())
{
auto x=q.top(); q.pop();
a[x]=++tim;
for(auto p=G.head[x];p;p=G.next[p])
{
indeg[G.to[p]]--;
if(indeg[G.to[p]]==0)q.emplace(G.to[p]);
}
}
}
int main()
{
ll u, v, i, j, k;
N=read(), M=read();
for(i=1;i<=M;i++)
{
u=read(), v=read();
G.adde(max(u,v),min(u,v),0);
indeg[min(u,v)]++;
}
BFS();
for(i=1;i<=N;i++)
{
ll mx=-1;
for(j=1;j<i;j++)if(a[j]<a[i])break;
if(j==i)f[i]=1;
for(j=i-1;j;j--)
{
if(a[j]>a[i])continue;
mx=max(mx,a[j]);
if(a[j]==mx)f[i]+=f[j];
}
}
ll mx=-1, ans=0;
for(i=N;i;i--)
{
mx=max(mx,a[i]);
if(a[i]==mx)ans+=f[i];
}
cout<<ans;
return 0;
}