0%

[CSGRound3] 出游

额,虽然这道题在你谷上的评分是蓝,但我觉得并不简单。

顺便让我复习了一下暑假讲过的知识点。

题目链接

题意

现在一共有 个人,其中第 个人有 个朋友,并且朋友关系是单向的。(即可能存在两个人A、B,使得 A 是 B 的朋友,但是 B 不是 A 的朋友)

现在有一场派对,持续若干天。第 天时,第 个人有 的概率参加派对,有 的概率不参加派对。

对于第 天来说,第 个人参加派对,当且仅当存在至少一个 的朋友 ,使得第 个人在第 天参加了派对。

求第 天时,参加派对的期望人数,对 取模。

题解

首先来考虑 的时候怎么做。

显然对于任何一个人 ,只要他有一个朋友在第 天时参加派对, 就会去参加派对。

这个时候我们可以直接算出 不参加派对的概率,从而算出 参加派对的概率。

现在来考虑

显然对于任何一个人 ,只要存在一个人 在第 天参加派对,并且 朋友的朋友, 就会去参加派对。

显然,这种“朋友套朋友”的关系不好叙述,我们换一种方式。

我们把一个人看作一个点,如果人 是人 的朋友,我们再连一条 的边。

这样子朋友的关系就很好叙述了。

对于 ,也就是对于每一个 ,只要存在一个人 在第 天参加派对,并且从 经过恰好两条边可以到达点 就会去参加派对。

如果 为任意数,那么对于任何一个人 ,只要存在一个人 在第 天参加派对,并且从 经过恰好 条边可以到达点 就回去参加派对。

现在如果我们对于每一个人 ,如果能求出所有的人 ,那么这个问题就解决了。

这个问题显然可以使用倍增 Floyd 来解决,我们设 表示第 个人走 条边,是否可以走到 ,转移的时候枚举中间点即可。

这样时间复杂度是 ,离满分还差一点。

这个时候我们能发现,对于每一个 ,它的值不是 就是 (废话。

所以我们可以使用bitset进行优化。

也就是说我们开若干个 bitset,其中 表示的是第 个人走 条边,走到的人的集合。

时间复杂度是 ,能过。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<bitset>
#include<queue>
#include<map>
#define inf 0x3f3f3f3f3f3f3f3fll
const int mod=998244353;
using namespace std;
using ll=long long;
using pli=pair<ll,int>;
using pi=pair<int,int>;
template<typename A>
using vc=vector<A>;
inline int read()
{
int s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
bitset<501>f[501][31];
bitset<501>num[501];
bitset<501>mem[501];
bool vis[501][501];
int p[501];
int n,m;
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++)
{
p[i]=read(),num[i][i]=1;int c=read();
for(int j=1;j<=c;j++) f[i][0][read()]=1;
}
for(int i=1;i<=30;i++) for(int u=1;u<=n;u++) for(int v=1;v<=n;v++)
if(f[u][i-1][v]) f[u][i]|=f[v][i-1];
for(int i=0;i<=30;i++) if(m&(1<<i))
{
for(int u=1;u<=n;u++) mem[u]=num[u],num[u].reset();
for(int u=1;u<=n;u++) for(int v=1;v<=n;v++) if(mem[u][v]==1)
num[u]|=f[v][i];
}
// for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) printf("%d%c",(int)num[i][j]," \n"[j==n]);
ll ans=0;
for(int i=1;i<=n;i++)
{
ll val=1;
for(int j=1;j<=n;j++) if(num[i][j]==1) val=val*(mod+1-p[j])%mod;
ans+=mod+1-val;
}
printf("%lld\n",ans%mod);
return 0;
}

感谢观看。