読者です 読者をやめる 読者になる 読者になる

AOJ2377 ThreeRooks

これで1200+以外は1問以上解いた事になった




概要

X*YのマスにK匹のうさぎがいる。互いに攻撃できないように3個のルークを置く場合の数をmod 10^9+7で求めよ

X,Y<=10^9,K<=10^5



解法

3個同じ所に並んでいる場合

2個並んでいて1個はその範囲にはない

3個が直角に並んでいる場合

を数えると解ける。
1,2個目は普通に求まって, 3個目は気合で平面走査する
平面走査では、区間全体にある値を足す,あるところを0にする,ある範囲の和を求める
が出来ると良いのでよくあるsegment treeを使う(座標圧縮はそんなに面倒でもない)

こういうのはvectorをもらって計算する関数を作っておくと、座標を入れ替えるだけで良くて嬉しみがある

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define rep(i,n) for(int i=0;i<n;++i)

const ll MOD=1000000007;
ll ret,p2,p6;
ll X,Y,K,zan;

inline ll c2(ll x){return x*(x-1)%MOD*p2%MOD;}
inline ll c3(ll x){return x*(x-1)%MOD*(x-2)%MOD*p6%MOD;}
inline void md(ll &x,ll y){x+=y;if(x>=MOD)x-=MOD;}

vector<int> xs;
inline int p(int x){return lower_bound(xs.begin(),xs.end(),x)-xs.begin();}
int sz;
struct segtree{
    vector<ll> dat1, dat2;
    void init(int n_){
	for(sz=1;sz<n_;sz*=2);
	dat1=vector<ll>(sz*2,0);
	dat2=vector<ll>(sz*2,0);  
    }
    void add(int a,int b,ll x,int k=0,int l=0,int r=sz){
	if(b<=l||r<=a)return ;
	if(a<=l&&r<=b)md(dat1[k],x);
	else{
	    md(dat2[k],x*(xs[min(b,r)]-xs[max(a,l)])%MOD);
	    add(a,b,x,k*2+1,l,(l+r)/2);
	    add(a,b,x,k*2+2,(l+r)/2,r);
	}
    }

    ll sum(int a,int b,int k=0,int l=0,int r=sz){
	if(b<=l||r<=a)return 0;
	if(a<=l&&r<=b)return (dat1[k]*(xs[r]-xs[l])+dat2[k])%MOD;
	else{
	    ll res=dat1[k]*(xs[min(b,r)]-xs[max(a,l)]);
	    res+=sum(a,b,k*2+1,l,(l+r)/2);
	    res+=sum(a,b,k*2+2,(l+r)/2,r);
	    return res%MOD;
	}
    }
}seg;

ll calc_23(ll h,ll w,vector<pii>& pt){
    ll s=0,my=h;
    sort(pt.begin(),pt.end());
    for(int i=0;i<pt.size();--my){
	int y=pt[i].fi,j=i;
	while(j<pt.size()&&pt[j].fi==y)++j;
	vector<int> v(j-i);
	for(int k=i;k<j;++k)v[k-i]=pt[k].se;
	md(s,(c3(v[0])+c2(v[0])*(zan-v[0]))%MOD);
	for(int k=1;k<v.size();++k){
	    ll t=v[k]-v[k-1]-1;
	    md(s,c3(t)+c2(t)*(zan-t)%MOD);
	}
	ll t=w-1-v.back();
	md(s,c3(t)+c2(t)*(zan-t)%MOD);
	i=j;
    }
    return (s+(my*c3(w)+(zan-w)*c2(w)%MOD*my))%MOD;
}

ll calc_L(ll h,ll w,vector<pii>& pt){
    sort(pt.begin(),pt.end());
    xs.resize(pt.size()*2);
    rep(i,pt.size()){xs[i*2]=pt[i].se;xs[i*2+1]=pt[i].se+1;}
    xs.pb(0);xs.pb(w);
    sort(xs.begin(),xs.end());
    xs.erase(unique(xs.begin(),xs.end()),xs.end());
    seg.init(xs.size());
    int rr=xs.size()-1;

    ll s=0,la=-1;
    for(int i=0;i<pt.size();){
	int y=pt[i].fi,j=i;
	ll g=seg.sum(0,rr);

	seg.add(0,rr,y-la-1);
	ll ng=seg.sum(0,rr);
	md(s,((w-1)*(g+ng-w)%MOD*(y-la-1)%MOD*p2)%MOD);
	while(j<pt.size()&&pt[j].fi==y)++j;
	vector<int> v(j-i);
	for(int k=i;k<j;++k)v[k-i]=pt[k].se;

	if(v[0]>1)md(s,(v[0]-1)*seg.sum(0,p(v[0]))%MOD);

	for(int k=1;k<v.size();++k){
	    ll t=v[k]-v[k-1]-1;
	    if(t>1)md(s,(t-1)*seg.sum(p(v[k-1]+1),p(v[k]))%MOD);
	}
	ll t=w-1-v.back();
	if(t>1)md(s,(t-1)*seg.sum(p(v.back()+1),rr)%MOD);
	seg.add(0,rr,1);
	for(int k=i;k<j;++k){
	    int l=p(pt[k].se);
	    ll x=seg.sum(l,l+1);
	    seg.add(l,l+1,-x);
	}
	i=j;
	la=y;
    }
    if(la!=h-1){
	ll num=h-1-la,t1=seg.sum(0,rr),t2=(t1+w*(num-1))%MOD;
	md(s,(w-1)*(t1+t2)%MOD*num%MOD*p2%MOD);
    }
    return s;
}

int main(){
    p2=(MOD+1)/2;p6=(MOD+1)/6;
    scanf("%lld%lld%lld",&X,&Y,&K);
    vector<pii> pt(K);
    rep(i,K){
	int x,y;
	scanf("%d%d",&x,&y);
	pt[i]=mp(x,y);
    }
    zan=(X*Y-K)%MOD;
    if(zan<3){puts("0");return 0;}
    ret=c3(zan);
    md(ret,MOD-calc_23(X,Y,pt));
    rep(i,K)swap(pt[i].fi,pt[i].se);
    md(ret,MOD-calc_23(Y,X,pt));
    rep(i,K)swap(pt[i].fi,pt[i].se);
    ret+=calc_L(X,Y,pt);
    rep(i,K)pt[i].fi=X-1-pt[i].fi;
    ret+=calc_L(X,Y,pt);
    printf("%d\n",ret%MOD);
    return 0;
}