计蒜客t30371 Inversion

链接

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值