BZOJ2727: [HNOI2012]双十字-树状数组

匿名 (未验证) 提交于 2019-12-03 00:22:01

传送门

给定一个RC的01 矩阵,要求计算出这个 01 矩阵中有多少个双十字。

双十字由两条水平的和一条竖直的“1”线段组成,要求满足以下几个限制:

1.两条水平的线段不能在相邻的两行。

2.竖直线段上端必须严格高于两条水平线段,下端必须严格低于两条水平线段。

3.竖直线段必须将两条水平线段严格划分成相等的两半。

4.上方的水平线段必须严格短于下方的水平线段。

输出双十字的个数 mod 1,000,000,009 的值。

R,C,N10000,RC1000000

Solution:

lr[i]表示从i开始表示最多能左右延伸多少

down[i]表示从i开始最多往下延伸多少

top[i]表示从i开始最多往上延伸多少

我们考虑枚举下端线段的中点j,然后对于每个上端线段的中点i,对答案的贡献有

len=1lr[j]min(len1,lr[i])top[i]down[j]

min不是很好处理,我们想办法去掉它

尝试分类讨论:

lr[i]lr[j]时,答案为(lr[i]lr[j]lr[i](lr[i]+1)2)top[i]down[j]

lr[i]lr[j]时,答案为lr[j](lr[j]1)2top[i]down[j]

对于每一列的连续的非0区间,我们只需要把关于i的东西全部记录下来,每次结合j进行计算即可

用树状数组维护3个值:

1.lr[i](lr[i]+1)2top[i]

2.lr[i]top[i]

3.top[i]

因为R和C 不确定,数组可能开不下,所以我们把矩阵压成一维

注意代码里是实时计算top
代码:

#include<cstdio> #include<iostream> #include<cstring> using namespace std; int l[10010],r[10010],lr[1200010],down[1200010],pos[1200010]; int tr[10010][3],n,m,p,ans,top=1; const int mod=1e9+9; int id(int x,int y){return (x-1)*m+y;} int add(int id,int i,int x) {     //cout<<id<<" "<<i<<" "<<x<<endl;     for (;i<=m;i+=(i&-i))     {         tr[i][id]+=x;         if (tr[i][id]>=mod) tr[i][id]-=mod;         if (tr[i][id]<0) tr[i][id]+=mod;     } } int query(int id,int i) {     int ans=0;     for (;i;i-=(i&-i))     {         ans+=tr[i][id];         if (ans>=mod) ans-=mod;         if (ans<0) ans+=mod;     }     return ans; } int main() {     scanf("%d%d%d",&n,&m,&p);     for (int x,y,i=1;i<=p;i++) scanf("%d%d",&x,&y),pos[id(x,y)]=1;     for (int i=1;i<=n;i++)     {         l[1]=(!pos[id(i,1)]);         for (int j=2;j<=m;j++) if (!pos[id(i,j)]) l[j]=l[j-1]+1;else l[j]=0;         r[m]=(!pos[id(i,m)]);         for (int j=m-1;j>=1;j--) if (!pos[id(i,j)]) r[j]=r[j+1]+1;else r[j]=0;         for (int j=1;j<=m;j++)             lr[id(i,j)]=min(l[j],r[j])-1;             //,cout<<i<<" "<<j<<" "<<lr[id(i,j)]<<endl;     }     for (int i=1;i<=m;i++)     {         down[id(n,i)]=(!pos[id(n,i)]);         for (int j=n-1;j>=1;j--)             if (!pos[id(j,i)]) down[id(j,i)]=down[id(j+1,i)]+1;     }     for (int i=1;i<=n*m;i++) if (down[i]) down[i]--;      for (int i=1;i<=m;i++)     {         for (int j=1;j<=n;j++)         {             if (pos[id(j,i)]){memset(tr,0,sizeof(tr));top=j+1;continue;}             ans=(1ll*query(0,lr[id(j,i)])*down[id(j,i)]+ans)%mod;             ans=(1ll*query(1,lr[id(j,i)])*down[id(j,i)]%mod*lr[id(j,i)]+ans)%mod;             ans=(1ll*(query(2,m)-query(2,lr[id(j,i)])+mod)*down[id(j,i)]%mod*lr[id(j,i)]*(lr[id(j,i)]-1)/2+ans)%mod;         //  cout<<ans<<endl;             if (j==1) continue;             if (pos[id(j-1,i)]) continue;             if (lr[id(j-1,i)])             {                 add(0,lr[id(j-1,i)],(1ll*(-lr[id(j-1,i)]*(lr[id(j-1,i)]+1)/2)*(j-1-top)%mod+mod)%mod);                 add(1,lr[id(j-1,i)],lr[id(j-1,i)]*(j-1-top));                 add(2,lr[id(j-1,i)],j-1-top);             }         }         memset(tr,0,sizeof(tr));top=1;     }     printf("%d",ans); }
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!