码迷,mamicode.com
首页 > 其他好文 > 详细

[HNOI2012]双十字

时间:2018-03-10 12:03:04      阅读:164      评论:0      收藏:0      [点我收藏+]

标签:正整数   hnoi   string   include   void   最小   线段   注意   blog   

题目描述

在C 部落,双十字是非常重要的一个部落标志。所谓双十字,如下面两个例子,由两条水平的和一条竖直的”1“线段组成,要求满足以下几个限制:技术分享图片 ![] 我们可以找到 5 个满足条件的双十字,分别如下: 技术分享图片 注意最终的结果可能很大,只要求输出双十字的个数 mod 1,000,000,009 的值·两条水平的线段不能在相邻的两行。·竖直线段上端必须严格高于两条水平线段,下端必须严格低于两条水平线段。 ·竖直线段必须将两条水平线段严格划分成相等的两半。·上方的水平线段必须严格短于下方的水平线段。 所以上面右边的例子是满足要求的最小的双十字。现在给定一个 R*C的01 矩阵,要求计算出这个 01 矩阵中有多少个双十字。例如下面这个例子,R=6,C=8,01 矩阵如下:

输入输出格式

输入格式:

 

第一行为用空格隔开的两个正整数 R和C,分别表示01矩阵的行数和列数。输入文件第二行是一个非负整数N,表示01矩阵中”0“的个数。接下来的N行,每行为用空格隔开的两个正整数x和y(1<=x<=R,1<=y<=C),表示(x,y)是一个”0“。数据保证N个”0“的坐标两两不同。数据保证R,C,N<=10,000,R*C<=1,000,000.(事实上R*C可能稍大于原设定)

 

输出格式:

 

D mod 1,000,000,009 的结果,其中D 为要求的 01矩阵中双十字的个数。

 

输入输出样例

输入样例#1: 复制
6  8
12
1  2
1  3
1  4
1  6
2  2
3  2
3  3
3  4
3  7
6  4
6  6
4  8
输出样例#1: 复制
5
因为10000×10000存不下,所以用编号代替坐标
首先DP求出L,R,U,D数组表示左右上下有多少个连续的1
一个点左右可拓展的长度应当是min(L,R)
枚举十字架中心在第几列
当了某一行,考虑以它作于下面一个横线的方案数
$\sum_{len=1}^{L[i]} min(L[j], len-1)×D[i]×(j-top)$
把min拆掉:
1.当len-1<=L[j],则$\sum_{len=1}^{L[i]} L[j]×D[i]×(j-top)$
2.当len-1>L[j],则$\sum_{len=1}^{L[i]} (len-1)×D[i]×(j-top)$
可以变成:
1.当L[j]<=L[i],贡献$(L[i]-(L[j]+1)/2)×D[i]×(j-top)×L[j]$
2.当L[j]>L[i],贡献$L[i]×(L[i]-1)/2×D[i]×(j-top)$
维护3个树状数组,分别维护:
$(j-top)$
$(j-top)*L[j]$
$(j-top)*L[j]*L[j]$
每一次碰到0或算完一列的答案要给树状数组清0
因为十字架上面的横线不能是i-1,所以考虑先算完i,再加入i-1
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cmath>
  5 using namespace std;
  6 int mp[1500001],U[1500001],D[1500001],L[1500001],R[1500001],n,r,c;
  7 int Mod=1e9+9,f1[50001],f2[50001],f3[50001],inv2,top,ans,ed;
  8 int get_id(int x,int y)
  9 {
 10   return c*(x-1)+y;
 11 }
 12 int qpow(int x,int y)
 13 {
 14   int res=1;
 15   while (y)
 16     {
 17       if (y&1) res=1ll*res*x%Mod;
 18       x=1ll*x*x%Mod;
 19       y>>=1;
 20     }
 21   return res;
 22 }
 23 void add1(int x,int d,int N)
 24 {
 25   if (x==0) return;
 26   while (x<=N)
 27     {
 28       f1[x]+=d;
 29       if (f1[x]>=Mod) f1[x]-=Mod;
 30       x+=x&(-x);
 31     }
 32 }
 33 void add2(int x,int d,int N)
 34 {
 35   if (x==0) return;
 36   while (x<=N)
 37     {
 38       f2[x]+=d;
 39       if (f2[x]>=Mod) f2[x]-=Mod;
 40       x+=x&(-x);
 41     }
 42 }
 43 void add3(int x,int d,int N)
 44 {
 45   if (x==0) return; 
 46   while (x<=N)
 47     {
 48       f3[x]+=d;
 49       if (f3[x]>=Mod) f3[x]-=Mod;
 50       x+=x&(-x);
 51     }
 52 }
 53 int query1(int x)
 54 {
 55   int s=0;
 56   while (x)
 57     {
 58       s+=f1[x];
 59       if (s>=Mod) s-=Mod;
 60       x-=(x&(-x));
 61     }
 62   return s;
 63 }
 64 int query2(int x)
 65 {
 66   int s=0;
 67   while (x)
 68     {
 69       s+=f2[x];
 70       if (s>=Mod) s-=Mod;
 71       x-=(x&(-x));
 72     }
 73   return s;
 74 }
 75 int query3(int x)
 76 {
 77   int s=0;
 78   while (x)
 79     {
 80       s+=f3[x];
 81       if (s>=Mod) s-=Mod;
 82       x-=(x&(-x));
 83     }
 84   return s;
 85 }
 86 int get_van(int x,int y,int N)
 87 {
 88   int t=L[get_id(x,y)];
 89   int as=0;
 90   int s2=query2(t),s3=query3(t);
 91     as=(1ll*t*s2%Mod-1ll*(s3+s2)%Mod*inv2%Mod+Mod);
 92   if (as>=Mod) as-=Mod;
 93   int s1=(query1(N)-query1(t)+Mod);
 94   if (s1>=Mod) s1-=Mod;
 95   as=(as+1ll*t*(t-1)%Mod*inv2%Mod*s1%Mod);
 96   if (as>=Mod) as-=Mod;
 97   return 1ll*as*D[get_id(x,y)]%Mod;
 98 }
 99 void update(int x,int y,int N)
100 {
101   int t=L[get_id(x,y)];
102   add1(t,x-top,N);
103   add2(t,1ll*(x-top)*t%Mod,N);
104   add3(t,1ll*(x-top)*t%Mod*t%Mod,N);
105 }
106 int main()
107 {int i,j,x,y,l;
108   cin>>r>>c;
109   cin>>n;
110   inv2=qpow(2,Mod-2);
111   memset(mp,-1,sizeof(mp));
112   for (i=1;i<=n;i++)
113     {
114       scanf("%d%d",&x,&y);
115       mp[get_id(x,y)]=0;
116     }
117   for (i=1;i<=r;i++)
118     {
119       for (j=1;j<=c;j++)
120       {
121         if (mp[get_id(i,j)]==-1)
122           mp[get_id(i,j)]=1;
123       }
124     }
125   for (i=1;i<=r;i++)
126     {
127       L[get_id(i,1)]=0;
128       for (j=2;j<=c;j++)
129       if (mp[get_id(i,j-1)]==1&&mp[get_id(i,j)]==1) L[get_id(i,j)]=L[get_id(i,j-1)]+1;
130       else L[get_id(i,j)]=0;
131       R[get_id(i,c)]=0;
132       for (j=c-1;j>=1;j--)
133       if (mp[get_id(i,j+1)]==1&&mp[get_id(i,j)]==1) R[get_id(i,j)]=R[get_id(i,j+1)]+1;
134       else R[get_id(i,j)]=0;
135       for (j=1;j<=c;j++)
136       L[get_id(i,j)]=min(L[get_id(i,j)],R[get_id(i,j)]);
137     }
138   for (j=1;j<=c;j++)
139     {
140       U[get_id(1,j)]=0;
141       for (i=2;i<=r;i++)
142       if (mp[get_id(i-1,j)]==1&&mp[get_id(i,j)]==1) U[get_id(i,j)]=U[get_id(i-1,j)]+1;
143       else U[get_id(i,j)]=0;
144       D[get_id(r,j)]=0;
145       for (i=r-1;i>=1;i--)
146       if (mp[get_id(i+1,j)]==1&&mp[get_id(i,j)]==1) D[get_id(i,j)]=D[get_id(i+1,j)]+1;
147       else D[get_id(i,j)]=0;
148     }
149   for (j=1;j<=c;j++)
150     {top=0;
151       ed=min(c-j,j-1);
152       for (l=0;l<=ed;l++)
153     f1[l]=f2[l]=f3[l]=0;
154       for (i=1;i<=r;i++)
155       {
156         if (top==0&&mp[get_id(i,j)]==1) top=i;
157         if (mp[get_id(i,j)]==0&&top)
158           {
159             for (l=0;l<=ed;l++)
160           f1[l]=f2[l]=f3[l]=0;
161             top=0;
162             continue;
163           }
164         if (top==0) continue;
165         if (i==top) continue;
166         if (i-top>=3&&L[get_id(i,j)]>1)
167           {
168             ans=ans+get_van(i,j,ed);
169             if (ans>=Mod) ans-=Mod;
170           }
171         if (i-1!=top&&L[get_id(i-1,j)]>0)
172           update(i-1,j,ed);
173       }
174     }
175   cout<<ans;
176 }

 

[HNOI2012]双十字

标签:正整数   hnoi   string   include   void   最小   线段   注意   blog   

原文地址:https://www.cnblogs.com/Y-E-T-I/p/8537602.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!