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;
}

感谢观看。