BZOJ 2727:[HNOI 2012]ダブルクロスツリー配列
10779 ワード
トランスファゲート
タイトル:
R∗C R∗Cの01行列が与えられ、この01行列の中にどれだけの二重十字があるかを計算する必要がある.
二重十字は、2つの水平線と1つの垂直な「1」線分で構成され、以下の制限を満たす必要があります.
1.2つの水平の線分は隣接する2行にはできません.
2.垂直線分の上端は2本の水平線分より厳格に高く、下端は2本の水平線分より厳格に低くなければならない.
3.垂直線分は、2つの水平線分を厳密に等しい半分に分割する必要があります.
4.上の水平線分は、下の水平線分よりも厳密に短くなければならない.
二重十字の個数mod 1000000009の値を出力します.
R,C,N≤10000,R∗C≤1000000 R , C , N ≤ 10000 , R ∗ C ≤ 1000000
Solution:
lr[i]l r[i]はiから最大で左右にどれだけ伸びるかを表す
down[i]d o w n[i]はiから最大どれだけ下に伸びるかを表す
top[i]はiから最大でどれだけ伸びるかを表す
下端セグメントの中点jを列挙し,次に各上端セグメントの中点iに対して答えへの寄与を考慮する.
∑lr[j]len=1min(len−1,lr[i])∗top[i]∗down[j] ∑ l e n = 1 l r [ j ] m i n ( l e n − 1 , l r [ i ] ) ∗ t o p [ i ] ∗ d o w n [ j ]
minはうまく処理できないので、何とかして取り除きます.
ディスカッションの分類を試みます.
lr[i]≦lr[j]l r[i]≦l r[j]の場合、答えは(lr[i]∗lr[j]−lr[i](lr[i]+1)2)∗top[i]∗down[j](lr[i]∗lr[j]−l r[i](l r[i]+1)2)∗t o p[i]∗d o w n[j]
>lr[i]>lr[j]>l r[i]>l r[j]の場合、lr[j]∗(lr[j]−1)2∗top[i]−down[j]l r[j]∗(l r[j]−1)2∗t o p[i]∗d o w n[j]
各列の連続する非0区間については,iに関するものをすべて記録し,jと結合して計算するたびに
3つの値をツリー配列で維持します.
1. −lr[i]∗(lr[i]+1)2∗top[i] − l r [ i ] ∗ ( l r [ i ] + 1 ) 2 ∗ t o p [ i ]
2. lr[i]∗top[i] l r [ i ] ∗ t o p [ i ]
3. top[i] t o p [ i ]
RとCは不確定で配列が開かない可能性があるので,行列を1次元に圧縮した.
注意コードにはリアルタイム計算topコードが含まれています.
タイトル:
R∗C R∗Cの01行列が与えられ、この01行列の中にどれだけの二重十字があるかを計算する必要がある.
二重十字は、2つの水平線と1つの垂直な「1」線分で構成され、以下の制限を満たす必要があります.
1.2つの水平の線分は隣接する2行にはできません.
2.垂直線分の上端は2本の水平線分より厳格に高く、下端は2本の水平線分より厳格に低くなければならない.
3.垂直線分は、2つの水平線分を厳密に等しい半分に分割する必要があります.
4.上の水平線分は、下の水平線分よりも厳密に短くなければならない.
二重十字の個数mod 1000000009の値を出力します.
R,C,N≤10000,R∗C≤1000000 R , C , N ≤ 10000 , R ∗ C ≤ 1000000
Solution:
lr[i]l r[i]はiから最大で左右にどれだけ伸びるかを表す
down[i]d o w n[i]はiから最大どれだけ下に伸びるかを表す
top[i]はiから最大でどれだけ伸びるかを表す
下端セグメントの中点jを列挙し,次に各上端セグメントの中点iに対して答えへの寄与を考慮する.
∑lr[j]len=1min(len−1,lr[i])∗top[i]∗down[j] ∑ l e n = 1 l r [ j ] m i n ( l e n − 1 , l r [ i ] ) ∗ t o p [ i ] ∗ d o w n [ j ]
minはうまく処理できないので、何とかして取り除きます.
ディスカッションの分類を試みます.
lr[i]≦lr[j]l r[i]≦l r[j]の場合、答えは(lr[i]∗lr[j]−lr[i](lr[i]+1)2)∗top[i]∗down[j](lr[i]∗lr[j]−l r[i](l r[i]+1)2)∗t o p[i]∗d o w n[j]
>lr[i]>lr[j]>l r[i]>l r[j]の場合、lr[j]∗(lr[j]−1)2∗top[i]−down[j]l r[j]∗(l r[j]−1)2∗t o p[i]∗d o w n[j]
各列の連続する非0区間については,iに関するものをすべて記録し,jと結合して計算するたびに
3つの値をツリー配列で維持します.
1. −lr[i]∗(lr[i]+1)2∗top[i] − l r [ i ] ∗ ( l r [ i ] + 1 ) 2 ∗ t o p [ i ]
2. lr[i]∗top[i] l r [ i ] ∗ t o p [ i ]
3. top[i] t o p [ i ]
RとCは不確定で配列が開かない可能性があるので,行列を1次元に圧縮した.
注意コードにはリアルタイム計算topコードが含まれています.
#include
#include
#include
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<" "<" "<<x<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<" "<" "<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<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);
}