[CSP-S模拟测试]:题(DP)

题目描述

由于出题人赶时间所以没办法编故事来作为背景。
一开始有$n$个苹果,$m$个人依次来吃苹果,第$i$个人会尝试吃$u_i$或$v_i$号苹果,具体来说分三种情况。
$\bullet 1.$两个苹果都还在,那么这个人将随便选一个苹果吃了。
$\bullet 2.$只有一个苹果,那么这个人将吃掉这个苹果。
$\bullet 3.$都不在了,这个人吃不到苹果就走了。
请问有多少对苹果$(i,j)(i<j)$满足它们两个都幸存下来的概率$>0$。


输入格式

第一行两个数$n,m$。
接下来$m$行,每行两个数$u_i,v_i$。


输出格式

一个数表示答案。


样例

样例输入:

4 3
1 2
3 4
2 3

样例输出:

1


数据范围与提示

样例解释:

只有$(1,4)$满足条件。

数据范围:

对于测试点$1\sim 5$:$n,m\leqslant 20$。
对于测试点$5\sim 8$:若把苹果看做点,人看做边,那么会形成一棵树。
对于测试点$9\sim 15$:$m\leqslant 400$。
对于测试点$16\sim 25$:无特殊限制。
对于所有的数据,$n\leqslant 400,m\leqslant 5\times 10^4$。


题解

考虑一个类似$DP$的做法,定义$f_k(S)$表示$k$个人来过之后,$S$集合的苹果是否都还没有被吃的概率,那么我们可以列出状态转移方程:

$\alpha.u_i,v_i\in S,f_k(S)=0$,都在这个集合肯定不行,因为这两个苹果不能共同存活。

$\beta.u_i\in S,f_k(S)=f_{k-1}(S\cup\{u_i\})$,相当与上一次可以有它,但是这一次就不能有了。

$\gamma.v_i\in S,f_k(S)=f_{k-1}(S\cup\{v_i\})$,同上。

$delta.u_i,v_i\notin S,f_k(S)=f_{k-1}(S)$,如果都不在,肯定没问题。

但是我们最后可能会得到好多的集合,选最小的一个?

肯定不行,那么我们考虑在转化一下思路。

逆着推,那么状态转移方程就变成了:

$\alpha.u_i,v_i\in S$,推不动了。

$\beta.u_i\in S,f_k(S\cup\{u_i\})=f_{k+1}(S)$。

$\gamma.v_i\in S,f_k(S\cup\{v_i\})=f_{k+1}()S$。

$delta.u_i,v_i\notin S,f_k(S)=f_{k+1}(S)$。

初始值$f_m(i)=1$。

时间复杂度:$\Theta(n\times n+n^2)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int n,m;
bool vis[401];
pair<int,int> e[50001];
bool bit[401][401];
int ans;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
		scanf("%d%d",&e[i].first,&e[i].second);
	for(int i=1;i<=n;i++)
	{
		bit[i][i]=1;
		for(int j=m;j;j--)
		{
			if(bit[i][e[j].first]&&bit[i][e[j].second])vis[i]=1;
			if(bit[i][e[j].first]||bit[i][e[j].second])bit[i][e[j].first]=bit[i][e[j].second]=1;
		}
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			for(int k=1;k<=n;k++)
				if(vis[i]||vis[j]||bit[i][k]&&bit[j][k])goto nxt;
			ans++;
			nxt:;
		}
	printf("%d",ans>>1);
	return 0;
}

rp++

posted @ 2019-10-01 19:48  HEOI-动动  阅读(282)  评论(0编辑  收藏  举报