题解:CF677E Vanya and Balloons

· · 题解

:::info[思路] 显然考虑十字中心,对于 $2\times4$ 个方向取到最后一个非 $0$,前缀和统计 $2$ 和 $3$。 但我们需要比较大小。考虑经典 trick,取对数。 具体的,将 $2^a3^b$ 转化为 $a+\log_2{3}\times b$,使用 `log2()` 库函数。 ::: 因为分讨很烦,我们考虑找到八个方向的通用算法。 - 使用类似二维中 dfs 使用的方向数组,避免分讨。 ```cpp ll d[8][2]={{-1,0},{1,0},{0,-1},{0,1}, {-1,-1},{1,1},{-1,1},{1,-1}}; ``` - 把最近 $0$ 的 $x$ 和 $y$ 不管哪个方向的全记上,取 $x$ 和 $y$ 差值的 $\max$ 即可,方向不用管。 - $2$ 和 $3$ 直接记录 $8$ 个方向的前缀和即可,不用记 $4$ 个方向再分讨。 抛去十字中心。特判所有数都是 $0$。 :::success[code] ```cpp #include<bits/stdc++.h> using namespace std; typedef long long ll; const ll N=1e3+10,p=1e9+7; ll d[8][2]={{-1,0},{1,0},{0,-1},{0,1}, {-1,-1},{1,1},{-1,1},{1,-1}},x[N][N][8]; ll y[N][N][8],a[N][N][8],b[N][N][8]; char s[N][N]; ll n,_a,_b,g; void f(ll i,ll j,ll k){ ll I=i+d[k][0],J=j+d[k][1]; a[i][j][k]=a[I][J][k]+(s[i][j]=='2'); b[i][j][k]=b[I][J][k]+(s[i][j]=='3'); if(s[i][j]!='0')//== x[i][j][k]=x[I][J][k],y[i][j][k]=y[I][J][k]; } int main(){ scanf("%lld",&n); for(ll i=1;i<=n;i++) scanf("%s",s[i]+1); for(ll i=0;i<=n+1;i++) for(ll j=0;j<=n+1;j++) for(ll k=0;k<8;k++) x[i][j][k]=i,y[i][j][k]=j; for(ll i=1;i<=n;i++) for(ll j=1;j<=n;j++) for(ll k=0;k<8;k+=2) f(i,j,k); for(ll i=n;i;i--) for(ll j=n;j;j--){ for(ll k=1;k<8;k+=2) f(i,j,k); for(ll l=0;l<8;l+=4){ ll t=n,A=0,B=0; for(ll k=l;k<l+4;k++) t=min(t,max(abs(i-x[i][j][k]),abs(j-y[i][j][k]))); for(ll k=l;k<l+4;k++){ ll I=i+t*d[k][0],J=j+t*d[k][1]; A+=a[i][j][k]-a[I][J][k]; B+=b[i][j][k]-b[I][J][k]; } if(s[i][j]=='0') continue; g=1;//` if(s[i][j]=='2') A-=3; if(s[i][j]=='3') B-=3;//` long double s=log2(3)*B+A,S=log2(3)*_b+_a; if(s>S) _a=A,_b=B; } } n=1; while(_a--) n=n*2%p; while(_b--) n=n*3%p;//- printf("%lld",g*n); return 0; } ``` ::: ~~模数打成 $998244353$ WA 了。~~