CF1086F Forest Fires

无限大的网格,第 $0$ 秒有 $n$ 个点燃烧,每一秒正燃烧的点会使与其八连通的点燃烧,每个点的权值为其最早燃烧的时间,求 $t$ 秒后的权值和。

$1 \le n \le 2000,1 \le t \le 10^8,-10^8 \le x,y \le 10^8$。

记 $f_i$ 为 $i$ 秒后燃烧格子的数量,那么答案为

$f_i$ 可用扫描线 $O(n\log n)$ 求解,可以求出所有 $f_i$ 代入上式获得答案,时间复杂度 $O(tn \log n)$。

由于每秒矩形都会扩大 $1$,并且所有矩形的大小相等,那么猜测 $f_i$ 与 $i$ 成某个简单的函数关系。矩形并的轮廓线的大小即为 $f_i$ 的增大量,发现轮廓线的大小成等差数列。准确来说,当矩形的相交关系不变时,$f_i$ 为关于 $i$ 的二次函数。总共有 $O(n^2)$ 种两两相交,它们将时间分成了 $O(n^2)$ 段,每段中 $f_i$ 的函数表达式不变。那么求出三个 $f_i$ 就可以得到本段中 $f_i$ 之和。时间复杂度 $O(n^3\log n)$。本方法足以通过 CF 原题。

发现矩形的交是好求的,考虑容斥,记 $g_{S,i}$ 为集合 $S$ 中的初始点在 $i$ 秒后形成矩形的交的大小,那么答案为

记 $dx,dy$ $S$ 中的点横纵坐标差的极大值,那么

通过推式子,$(-1)^{|S|+1}(tg_{S,t} - \sum_{i=0}^{t-1} g_{S,i})$ 可以 $O(1)$ 求解,记为 $G_S$。

进一步观察,若 $S$ 中存在一个点 $x$ 使得删去这个点后横纵坐标差的极大值不变,那么 $G_{S} + G_{S \setminus \{x\}} = 0$(符号相反),那么合法的 $S$ 大大减少,事实上只有 $O(n^2)$ 个。

将节点按横坐标排序,枚举 $i < j$ 框定横坐标差的极大值。需要判断 $(i,j)$ 中是否存在点的纵坐标在 $i,j$ 纵坐标内,如果存在那么包含 $i,j$ 的集合都不合法,否则将 $G_{\{i,j\}}$ 计入答案,并找到 $(i,j)$ 中 $i,j$ 纵坐标的前驱/后继,枚举它们是否计入 $S$ 计入答案。用 set 实现,时间复杂度 $O(n^2\log n)$。用链表实现可以做到 $O(n^2)$。

有一个细节是对于横/纵坐标相等的点,发现怎样钦定它们的顺序都是对的。

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
#include<bits/stdc++.h>
using namespace std;

#define ar(x) array<int,x>
const int N=2e3+100,INF=1e9,MOD=998244353,inv3=332748118;
inline void mod(int &x){ if(x>=MOD) x-=MOD; if(x<0) x+=MOD; }
inline int qm(int x,int y=MOD-2){ int res=1; for(;y;y>>=1,x=1ll*x*x%MOD) if(y&1) res=1ll*res*x%MOD; return res; }
#define gc getchar()
#define rd read()
inline int read(){
int x=0,f=0; char c=gc;
for(;c<'0'||c>'9';c=gc) f|=(c=='-');
for(;c>='0'&&c<='9';c=gc) x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}

int n,t,ans=0; ar(2) a[N]; set<int> s;

int f(int x,int y,int i){ return 1ll*max(0,(2*i-x+1))*max(0,(2*i-y+1))%MOD; }
int S1(int x){ return 1ll*x*(x+1)/2%MOD; }
int S2(int x){ return 1ll*x*(x+1)/2%MOD*(2*x+1)%MOD*inv3%MOD; }
int calc(int x,int y){
int res=1ll*t*f(x,y,t)%MOD,l=(max(x,y)+1)/2,r=t-1;
if(l>r) return res;
mod(res-=4ll*(S2(r)-S2(l-1))%MOD);
mod(res+=2ll*(x-1+y-1)*(S1(r)-S1(l-1))%MOD);
mod(res-=1ll*(x-1)*(y-1)%MOD*(r-l+1)%MOD);
return res;
}

void solve(int i,int j){
int dx=a[j][0]-a[i][0],p=INF,q=INF;
if(a[i][1]>a[j][1]) swap(i,j);
auto it1=s.upper_bound(a[j][1]);
auto it2=s.lower_bound(a[i][1]);
if(it1!=it2) return;
mod(ans-=calc(dx,a[j][1]-a[i][1]));
if(it1!=s.end()) mod(ans+=calc(dx,*it1-a[i][1])),p=*it1;
if(it2!=s.begin()) --it2,mod(ans+=calc(dx,a[j][1]-*it2)),q=*it2;
if(p!=INF&&q!=INF) mod(ans-=calc(dx,p-q));
}

void solve(){
n=rd,t=rd,ans=1ll*n*calc(0,0)%MOD;
for(int i=1;i<=n;++i) a[i]={rd,rd}; sort(a+1,a+n+1);

for(int i=1;i<=n;++i){
s.clear();
for(int j=i+1;j<=n;++j)
solve(i,j),s.insert(a[j][1]);
}
printf("%d\n", ans);
}

int main(){

int T=1;
while(T--) solve();

return 0;
}