[cf1086F]Forest Fires

记$f(i)$表示时刻$i$时着火的格子数,则答案即$t\cdot f(t)-\sum_{i=0}^{t-1}f(i)$

关于$f(i)$,即对所有点为中心、边长为$2i+1$​的矩形求并,容斥可得
$$
f(i)=\sum_{S\subseteq [1,n],S\ne \empty}(-1)^{|S|-1}\max(2i+1-d_{x}(S),0)\max(2i+1-d_{y}(S),0)
$$
(其中$d_{x/y}(S)$分别表示$S$中所有点$x$或$y$坐标的极差)

注意到可行的$d_{x}(S)$或$d_{y}(S)$均仅有$o(n^{2})$个,因此$f(i)$是一个$o(n^{2})$段的二次函数

对于每一段,仅需求出三个$f(i)$即可得到该函数,且单次(矩形求并)复杂度为$o(n\log n)$

总复杂度为$o(n^{3}\log n)$,可以通过

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 60
 4 #define mod 998244353
 5 #define ll long long
 6 #define pii pair<int,int>
 7 #define L (k<<1)
 8 #define R (L+1)
 9 #define mid (l+r>>1)
10 int T,n,t,ans,x[N],y[N],yl[N],yr[N],tag[N<<3],g[N<<3],f[N<<3];
11 vector<int>v0,vx,vy;vector<pii>v[N<<1];
12 int S1(int k){
13     return (ll)k*(k+1)%mod*(mod+1>>1)%mod;
14 }
15 int S2(int k){
16     return (ll)k*(k+1)%mod*((k<<1)+1)%mod*(mod+1)/6%mod;
17 }
18 int findx(int k){
19     return lower_bound(vx.begin(),vx.end(),k)-vx.begin()+1;
20 }
21 int findy(int k){
22     return lower_bound(vy.begin(),vy.end(),k)-vy.begin()+1;
23 }
24 void update(int k,int l,int r,int x,int y,int z){
25     if ((l>y)||(x>r))return;
26     if ((x<=l)&&(r<=y)){
27         tag[k]+=z,f[k]=(tag[k] ? vy[r]-vy[l-1] : g[k]);
28         return;
29     }
30     update(L,l,mid,x,y,z),update(R,mid+1,r,x,y,z);
31     g[k]=f[L]+f[R],f[k]=(tag[k] ? vy[r]-vy[l-1] : g[k]);
32 }
33 int calc(int k){
34     vx.clear(),vy.clear();
35     for(int i=1;i<=(n<<1);i++)v[i].clear();
36     for(int i=1;i<=n;i++){
37         vx.push_back(x[i]-k),vx.push_back(x[i]+k+1);
38         vy.push_back(y[i]-k),vy.push_back(y[i]+k+1);
39     }
40     sort(vx.begin(),vx.end()),sort(vy.begin(),vy.end());
41     for(int i=1;i<=n;i++){
42         yl[i]=findy(y[i]-k),yr[i]=findy(y[i]+k+1)-1;
43         v[findx(x[i]-k)].push_back(make_pair(i,1));
44         v[findx(x[i]+k+1)].push_back(make_pair(i,-1));
45     }
46     int ans=0;
47     for(int i=1;i<=(n<<1);i++){
48         for(pii j:v[i])update(1,1,(n<<1)-1,yl[j.first],yr[j.first],j.second);
49         if (i<(n<<1))ans=(ans+(ll)(vx[i]-vx[i-1])*f[1])%mod;
50     }
51     return ans;
52 }
53 int calc(int l,int r){
54     if (l>r)return 0;
55     if (l==r)return calc(l);
56     if (l+1==r)return (calc(l)+calc(l+1))%mod;
57     int c=calc(l),s1=calc(l+1),s2=calc(l+2);
58     s1=(s1-c+mod)%mod,s2=(s2-c+mod)%mod;
59     int a=((ll)s2*(mod+1>>1)-s1+mod)%mod,b=(s1-a+mod)%mod;
60     return ((ll)a*S2(r-l)+(ll)b*S1(r-l)+(ll)c*(r-l+1))%mod;
61 }
62 int main(){
63     scanf("%d%d",&n,&t);
64     for(int i=1;i<=n;i++)scanf("%d%d",&x[i],&y[i]);
65     ans=(ll)t*calc(t)%mod;
66     v0.push_back(t);
67     for(int i=1;i<=n;i++)
68         for(int j=i+1;j<=n;j++)
69             v0.push_back(max(abs(x[i]-x[j]),abs(y[i]-y[j]))+1>>1);
70     sort(v0.begin(),v0.end());
71     int l=0;
72     for(int i=0;(i<v0.size())&&(v0[i]<=t);i++)ans=(ans-calc(l,v0[i]-1)+mod)%mod,l=v0[i];
73     printf("%d\n",ans);
74     return 0;
75 }
View Code

交换求和顺序,答案也即$\sum_{S\subseteq [1,n],S\ne \empty}(-1)^{|S|-1}F(d_{x}(S),d_{y}(S))$

其中$F(x,y)$为后式的总贡献(带系数),简单推导后可以$o(1)$计算

将所有点按$x$坐标排序(相同时任意),并枚举$S$(在序列中)最左和最右两点

此时已经确定$x$坐标的两个极值,并考虑枚举$y$坐标的两个极值,从而确定$d_{x/y}(S)$

四个极值即构成一个矩形,并注意到以下性质:

若矩形内除上下边界和选定的两点外存在其余的点,该点并不影响$d_{x/y}(S)$,进而使得贡献抵消

换言之,上下边界均至多"拓展"一次,总共仅有$2\times 2=4$种可能

具体实现中,可以倒序枚举右侧的点,并用链表维护两者之间的点($y$坐标)即可

另外,关于$S$的$(-1)^{|S|-1}$之和,可以通过全体(为0)减去上/下边界全不选的情况

总复杂度为$o(n^{2})$,可以通过

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 60
 4 #define mod 998244353
 5 #define ll long long
 6 #define fi first
 7 #define se second
 8 int T,n,t,ans,b[N],cnt[N],pre[N],nex[N];
 9 pair<int,int>a[N];
10 int S1(int k){
11     return (ll)k*(k+1)%mod*(mod+1>>1)%mod;
12 }
13 int S2(int k){
14     return (ll)k*(k+1)%mod*((k<<1)+1)%mod*(mod+1)/6%mod;
15 }
16 int calc(int x,int y){
17     x=(t<<1)+1-x,y=(t<<1)+1-y;
18     if ((x<0)||(y<0))return 0;
19     int d=(min(x,y)>>1),ans=(ll)t*x%mod*y%mod;
20     ans=(ans-4LL*S2(d)%mod+mod)%mod;
21     ans=(ans+2LL*S1(d)*(x+y))%mod;
22     ans=(ans-(ll)d*x%mod*y%mod+mod)%mod;
23     return ans;
24 }
25 int main(){
26     scanf("%d%d",&n,&t);
27     for(int i=1;i<=n;i++){
28         scanf("%d%d",&a[i].fi,&a[i].se);
29         b[i]=a[i].se;
30     }
31     sort(a+1,a+n+1),sort(b+1,b+n+1);
32     for(int i=1;i<=n;i++)a[i].se=lower_bound(b+1,b+n+1,a[i].se)-b;
33     ans=(ll)n*calc(0,0)%mod;
34     for(int i=1;i<=n;i++){
35         memset(cnt,0,sizeof(cnt));
36         for(int j=i;j<=n;j++)cnt[a[j].se]++;
37         int lst=0;
38         for(int j=1;j<=n;j++)
39             if (cnt[j])pre[j]=lst,nex[lst]=j,lst=j;
40         pre[0]=lst,nex[lst]=0;
41         for(int j=n;j>i;j--){
42             int k=a[j].se;
43             if (--cnt[k]>(k==a[i].se))continue;
44             if (cnt[a[i].se]==1){
45                 int l=a[j].fi-a[i].fi,x=a[i].se,y=a[j].se;
46                 if (x>y)swap(x,y);
47                 if ((x==y)||(nex[x]==y)){
48                     ans=(ans-calc(l,b[y]-b[x])+mod)%mod;
49                     if (pre[x])ans=(ans+calc(l,b[y]-b[pre[x]]))%mod;
50                     if (nex[y])ans=(ans+calc(l,b[nex[y]]-b[x]))%mod;
51                     if ((pre[x])&&(nex[y]))ans=(ans-calc(l,b[nex[y]]-b[pre[x]])+mod)%mod;
52                 }
53             }
54             if (!cnt[k])pre[nex[k]]=pre[k],nex[pre[k]]=nex[k];
55         }
56     }
57     printf("%d\n",ans);
58     return 0;
59 }
View Code

 

posted @ 2022-05-01 17:36  PYWBKTDA  阅读(115)  评论(0编辑  收藏  举报