[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 }
交换求和顺序,答案也即$\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 }