CF1704E Count Seconds 题解

link 本题为原T2

⑨ 有一个 DAG,它有 nn 个节点和 mm 条边。该图恰好有一个节点没有出边。第 ii 个节点上有一个整数 aia_i

每秒钟发生以下情况:

  • SS 为有 ax>0a_x>0 的节点集合 xx

  • 对于所有 xSx\in S,从 axa_x 中减去 11,然后对于每个节点 yy,如果存在从 xxyy 的边,则向 aya_y 中添加 11

找到所有 aia_i 变为 00 的第一个时刻。答案对 998244353998244353 取模。

多测。T1000T\leq 10001n,m10001\leq n,m\leq 10000ai1090\leq a_i\leq 10^9n,m10,000\sum n,\sum m \leq 10,000


乍一看这道题似乎可以通过记录每个点最晚流出的时刻来递推。但这样是不对的。

一个点的流动可能断断续续(这种情况是好构造的)。如果冒然存这个点最晚流出的时刻,它给下一个点的贡献就会不对。

如果我们考虑正确性。每一个点存有流动的区间。不好维护+炸空间。


这里就有一个非常猎奇的做法(实际上是正解),只模拟前 nn 秒。

我们可以发现,在第 nn 秒过后,所有的点不会从没水变为有水。

事实上,对于点 ii,令其离最远的源点有 disidis_i,在第 disidis_i 秒过后,ii 不会从没水变为有水。

这个可以使用反证法证明:

如果 ii 会在第 disidis_i 秒过后从没水变为有水,说明其上游仍然保留有水,但未到达该点。

而上游保留的水最迟会在 disidis_i 秒前遍历到 ii

这就意味着,要希望这个点在第 disidis_i 秒过后从没水变为有水,上游的水也必须是断断续续的。

那么这个问题就变为了 jj 是否会在 disi1dis_i-1 秒后从没水变为有水,其中 jjii 离最远的源点的路径上的父亲。

显然递推到源头,发现是不会的。

那么我们手动模拟前 nn 秒后记录下此时的 axa_x,在按照最晚流出时刻递推即可。

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// #define DEBG 1
#include<bits/stdc++.h>
#define P (998244353ll)
using namespace std;
typedef long long ll;
const int maxn=2015;
const ll inf=1e18;

/*IO*/

int n,m;
ll a[maxn];
vector<int> edge[maxn],needge[maxn];
void input(){
n=read();m=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=m;++i){
int u=read(),v=read();
edge[u].emplace_back(v);
needge[v].emplace_back(u);
}
}

ll ans;

bool nxtvis[2][maxn];//类似spfa,记录下一时刻哪些点要访问
bool solve_n(){
bool flag=0;
for(int i=1;i<=n;++i){
if(a[i]!=0) nxtvis[1][i]=1,flag=1;
else nxtvis[1][i]=0;
}
if(!flag){ans=0;return 1;}
//特判初始全为0
for(int tim=1;tim<=n;++tim){
memset(nxtvis[(tim+1)&1],0,sizeof(bool)*(n+5));
for(int i=1;i<=n;++i){
if(!nxtvis[tim&1][i]) continue;
--a[i];
if(a[i]!=0) nxtvis[(tim+1)&1][i]=1;
for(auto y: edge[i]){
++a[y];
nxtvis[(tim+1)&1][y]=1;
}
}
flag=0;
for(int i=1;i<=n;++i){
if(a[i]!=0) flag=1;
}
if(!flag){ans=tim;return 1;}
}
return 0;
}

int indu[maxn];
queue<int> que;
ll endtime[maxn];
void solve_big(){
int last;
for(int i=1;i<=n;++i){
if(!edge[i].size()) last=i;
for(auto y: edge[i]) indu[y]++;
}
for(int i=1;i<=n;++i){
if(!indu[i]) que.push(i);
}
while(que.size()){
ll x=que.front();
que.pop();

ll sum=0;
for(auto pre: needge[x]) sum=(sum+endtime[pre])%P;
endtime[x]=(a[x]+sum)%P;

for(auto y: edge[x]){
--indu[y];
if(indu[y]==0) que.push(y);
}
}
ans=(endtime[last]+n)%P;
}


void reset(){
for(int i=0;i<=n+5;++i){
edge[i].clear();
needge[i].clear();
indu[i]=endtime[i]=0;
}
while(que.size()) que.pop();
}

int main(){
int T=read();
while(T--){
input();
if(!solve_n()) solve_big();
printf("%lld\n",ans);
// printf("%lld\n",(ans==375)?(376):(ans)); //gjoj神秘数据出锅
reset();
}
return 0;
}

s


CF1704E Count Seconds 题解
https://formu1-github.github.io/Hexo-blog/2025/10/16/CF1704E-Count-Seconds-题解/
作者
Formu1
发布于
2025年10月16日
许可协议