NKOJ 5140 大吉大利 晚上吃鸡

问题描述

何老板养了n只鸡(编号1到n)。何老板打算从今天开始,连续m晚都吃鸡。
每晚,何老板会选一对指定编号的鸡出来。若两只鸡都活着,那么他会随便吃掉其中一只;若只有一只活着,另一只之前已经被吃了,就吃还活着那只;若两只鸡都已被吃掉了,当晚就不吃鸡。
何老板想知道,m天后,可能有多少对鸡同时活着?请你帮他算一算。(如果一对鸡存活的概率>0,我们认为他们可能活着)

输入格式

第一行,两个整数n和m
接下来m行,每行两个整数x和y,第i行表示第i天选出的两只鸡的编号。

输出格式

一行,一个整数,表示最后还存活的鸡的对数。

我们来分析一下第 x x x鸡能够存活下来的条件。

假设与第 x x x鸡配对的鸡按照顺序分别为 y 1 , y 2 , . . . , y k y_1,y_2,...,y_k y1,y2,...,yk

x x x y k y_k yk中, x x x要存活,那么 y k y_k yk就要被吃

x x x y k − 1 y_{k-1} yk1中, x x x要存活,那么 y k − 1 y_{k-1} yk1就要被吃

x x x y 1 y_1 y1中, x x x要存活,那么 y 1 y_1 y1就要被吃。

也就是说,如果某只鸡要存活下来,那么与它配对的所有其他鸡都要被吃。

所以我们设立一个状态 l i v [ x ] [ y ] = 1 liv[x][y]=1 liv[x][y]=1,表示第 x x x只要存活下来,第 y y y只鸡必须活着等着为 x x x牺牲被吃。

那么第 x x x只鸡不能存活的条件呢?

假设第 a a a只鸡与第 b b b只鸡配对了,并且 l i v [ x ] [ a ] = = l i v [ x ] [ b ] = = 1 liv[x][a]==liv[x][b]==1 liv[x][a]==liv[x][b]==1

也就是说 x x x要存货下来,那么 a a a b b b都要存活下来,但是 a , b a,b a,b只能存活下来一个,于是在这种情况下第 x x x只鸡必死。

接下来讨论求解,也就是哪些对鸡可以存活。

我们假设这对鸡为 x , y x,y x,y

那么这对鸡的存活条件为如果存在 l i v [ x ] [ z ] = 1 liv[x][z]=1 liv[x][z]=1,那么 l i v [ y ] [ z ] ! = 1 liv[y][z]!=1 liv[y][z]!=1

时间复杂度: O ( n 3 + n m ) O(n^3+nm) O(n3+nm)

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define db double
#define sg string
#define ll long long
#define rel(i,x,y) for(ll i=(x);i<(y);i++)
#define rep(i,x,y) for(ll i=(x);i<=(y);i++)
#define red(i,x,y) for(ll i=(x);i>=(y);i--)
#define res(i,x) for(ll i=head[x];i;i=nxt[i])
using namespace std;

const ll N=405;
const ll M=1e5+5;
const ll Inf=1e18;
const ll Mod=1e9+7;
const db Eps=1e-10;

ll n,m,ans,a[M],b[M],flg[N],liv[N][N];

inline ll read() {
	ll x=0;char ch=getchar();bool f=0;
	while(ch>'9'||ch<'0'){if(ch=='-')f=1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return f?-x:x;
}

int main() {
	n=read(),m=read();
	
	rep(i,1,m) a[i]=read(),b[i]=read();
	
	rep(i,1,n) {
		liv[i][i]=1;
		
		red(j,m,1) {
			ll fa=liv[i][a[j]],fb=liv[i][b[j]];
			
			if(fa&&fb) {
				flg[i]=1;break;
			} else {
				if(fa) liv[i][b[j]]=1;
				if(fb) liv[i][a[j]]=1;
			}
		}
	}
	
	rep(i,1,n) if(!flg[i]) rep(j,i+1,n) if(!flg[j]) {
		ll flag=1;
		
		rep(k,1,n) if(liv[i][k]&&liv[j][k]) {
			flag=0;break ;
		}
		
		if(flag) ans++;
	}
	
	printf("%lld\n",ans);

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值