保卫艾尔

题目描述

凯莱克斯研制出的新防御矩阵系统的建立方式是这样的:他用艾尔上的 n座水晶塔为基础建立多层防御矩阵,如果将艾尔视为一个 直角坐标系,则每座水晶塔都有一个坐标(xi,yi) 。
一层防御矩阵是一个四边形,四边形的四个顶点都是一座水晶塔,而且四边形两条对角线分别平行于x 轴和y 轴。两条对角线的交 点为防御矩阵的中心。中心必须位于防御矩阵的内部,同时也不能位于四边形的边上(即该四边形是一个严格的凸四边形)。注 意,防御矩阵的中心不一定有水晶塔。
一个防御矩阵系统由多层防御矩阵组成,一个系统内的所有的防御矩阵都拥有相同的中心,且任意两个四边形不能相交。现在,凯 莱克斯想让你帮他计算大主教给他的三个问题:
1. 如果把层数最多的防御矩阵系统称为最优系统,那最优系统有多少层防御矩阵?
2. 有多少个地点可以作为最优系统的中心?
3. 有多少种建立最优系统的方案?
当然如果你觉得这些问题比较难可以先回答其中的一个或者两个。

扫描线

我们用扫描线做,比如对着x坐标扫。
用线段树维护,叶子表示一个y坐标在扫描线左的点数与在扫描线右的点数的最小值,以及对应组合数。而区间维护最大值,最大值个数及对应组合数的和。
中心要么建点上要么建两点中间,考虑后者枚举一个点,那么得到上面那个点,于是要对一个区间做询问,用线段树获得。
这可以解决第一问,但是如果左右被上下所限制(上下可拓展过小),取出的个数及组合数不能用。
因此可以两个坐标反过来再扫一遍。
关于两边拓展相同,在其中一次扫考虑另外一次不考虑即可。
线段树的询问最好把三个值的询问打在一起。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=500000+10,mo=1000000007;
struct dong{
    int x,y;
} a[maxn],aa[maxn];
int tree[maxn*4],cnt[maxn*4],num[maxn*4],sum[maxn*4],wz[5000+10][5000+10];
int cntx[maxn],cnty[maxn],b[maxn],fac[maxn],inv[maxn];
int d[maxn],c[maxn],e[maxn];
int i,j,k,l,r,t,n,m,ans1,ans2,ans3,czy,tx,ty,p;
int an1,an2,an3;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int quicksortmi(int x,int y){
    if (!y) return 1;
    int t=quicksortmi(x,y/2);
    t=(ll)t*t%mo;
    if (y%2) t=(ll)t*x%mo;
    return t;
}
bool cmp(dong a,dong b){
    return a.x<b.x||a.x==b.x&&a.y<b.y;
}
bool cmp2(dong a,dong b){
    return a.y<b.y||a.y==b.y&&a.x<b.x;
}
int qry1(int p,int l,int r,int a,int b){
    if (a>b) return 0;
    if (l==a&&r==b) return tree[p];
    int mid=(l+r)/2;
    if (b<=mid) return qry1(p*2,l,mid,a,b);
    else if (a>mid) return qry1(p*2+1,mid+1,r,a,b);
    else return max(qry1(p*2,l,mid,a,mid),qry1(p*2+1,mid+1,r,mid+1,b));
}
int qry2(int p,int l,int r,int a,int b){
    if (a>b) return 0;
    if (l==a&&r==b) return num[p];
    int mid=(l+r)/2;
    if (b<=mid) return qry2(p*2,l,mid,a,b);
    else if (a>mid) return qry2(p*2+1,mid+1,r,a,b);
    else{
        int j=qry1(p*2,l,mid,a,mid),k=qry1(p*2+1,mid+1,r,mid+1,b);
        if (j>k) return qry2(p*2,l,mid,a,mid);
        else if (j<k) return qry2(p*2+1,mid+1,r,mid+1,b);
        else return (qry2(p*2,l,mid,a,mid)+qry2(p*2+1,mid+1,r,mid+1,b))%mo;
    }
}
int qry3(int p,int l,int r,int a,int b){
    if (a>b) return 0;
    if (l==a&&r==b) return sum[p];
    int mid=(l+r)/2;
    if (b<=mid) return qry3(p*2,l,mid,a,b);
    else if (a>mid) return qry3(p*2+1,mid+1,r,a,b);
    else{
        int j=qry1(p*2,l,mid,a,mid),k=qry1(p*2+1,mid+1,r,mid+1,b);
        if (j>k) return qry3(p*2,l,mid,a,mid);
        else if (j<k) return qry3(p*2+1,mid+1,r,mid+1,b);
        else return (qry3(p*2,l,mid,a,mid)+qry3(p*2+1,mid+1,r,mid+1,b))%mo;
    }
}
void qry(int p,int l,int r,int a,int b){
    if (a>b) return;
    if (l==a&&r==b){
        if (tree[p]>an1){
            an1=tree[p];
            an2=num[p];
            an3=sum[p];
        }
        else if (tree[p]==an1){
            an2=(an2+num[p])%mo;
            an3=(an3+sum[p])%mo;
        }
        return;
    }
    int mid=(l+r)/2;
    if (b<=mid) qry(p*2,l,mid,a,b);
    else if (a>mid) qry(p*2+1,mid+1,r,a,b);
    else{
        qry(p*2,l,mid,a,mid);
        qry(p*2+1,mid+1,r,mid+1,b);
    }
}
int C(int n,int m){
    if (n<m) return 0;
    return (ll)fac[n]*inv[m]%mo*inv[n-m]%mo;
}
void change(int p,int l,int r,int a){
    if (l==r){
        cnt[p]++;
        tree[p]=min(cnt[p],cnty[l]-cnt[p]);
        num[p]=1;
        sum[p]=(ll)C(cnt[p],tree[p])*C(cnty[l]-cnt[p],tree[p])%mo;
        return;
    }
    int mid=(l+r)/2;
    if (a<=mid) change(p*2,l,mid,a);else change(p*2+1,mid+1,r,a);
    tree[p]=max(tree[p*2],tree[p*2+1]);
    if (tree[p*2]>tree[p*2+1]) num[p]=num[p*2],sum[p]=sum[p*2];
    else if (tree[p*2]<tree[p*2+1]) num[p]=num[p*2+1],sum[p]=sum[p*2+1];
    else num[p]=(num[p*2]+num[p*2+1])%mo,sum[p]=(sum[p*2]+sum[p*2+1])%mo;
}
void brute(){
    k=1;
    fo(i,1,tx){
        t=0;
        fo(j,1,ty){
            l=cntx[i]-t;
            if (i==a[k].x&&j==a[k].y) l-=wz[i][j];
            r=cnty[j]-cnt[j];
            if (i==a[k].x&&j==a[k].y) r-=wz[i][j];
            p=min(l,min(r,min(cnt[j],t)));
            if (p>ans1){
                ans1=p;
                ans2=1;
                ans3=(ll)C(t,p)*C(l,p)%mo*C(cnt[j],p)%mo*C(r,p)%mo;
            }
            else if (p==ans1){
                ans2=(ans2+1)%mo;
                ans3=(ans3+(ll)C(t,p)*C(l,p)%mo*C(cnt[j],p)%mo*C(r,p)%mo)%mo;
            }
            while (k<=n&&i==a[k].x&&j==a[k].y){
                cnt[j]++;
                t++;
                k++;
            }
        }
    }
    printf("%d",ans1);
    if (czy>=2) printf(" %d",ans2);
    if (czy==3) printf(" %d",ans3);
    printf("\n");
}
void clear(){
    int i;
    fo(i,1,ty*4) tree[i]=cnt[i]=num[i]=sum[i]=0;
}
int main(){
    freopen("aiur.in","r",stdin);freopen("aiur.out","w",stdout);
    n=read();czy=read();
    fo(i,1,n) a[i].x=read(),a[i].y=read();
    fo(i,1,n) b[i]=a[i].x;
    sort(b+1,b+n+1);
    tx=unique(b+1,b+n+1)-b-1;
    fo(i,1,n) a[i].x=lower_bound(b+1,b+tx+1,a[i].x)-b;
    fo(i,1,n) b[i]=a[i].y;
    sort(b+1,b+n+1);
    ty=unique(b+1,b+n+1)-b-1;
    fo(i,1,n) a[i].y=lower_bound(b+1,b+ty+1,a[i].y)-b;
    fo(i,1,n) cntx[a[i].x]++,cnty[a[i].y]++;
    /*if (n<=5000){
        fo(i,1,n) wz[a[i].x][a[i].y]++;
    }*/
    fac[0]=1;
    fo(i,1,n) fac[i]=(ll)fac[i-1]*i%mo;
    inv[n]=quicksortmi(fac[n],mo-2);
    fd(i,n-1,0) inv[i]=(ll)inv[i+1]*(i+1)%mo;
    /*if (n<=5000){
        brute();
        return 0;
    }*/
    //sort(a+1,a+n+1,cmp);
    fo(i,1,ty) d[i]=0;
    fo(i,1,n) d[a[i].y]++;
    fo(i,1,ty) d[i]+=d[i-1];
    fd(i,n,1) c[d[a[i].y]--]=i;
    fo(i,1,tx) d[i]=0;
    fo(i,1,n) d[a[i].x]++;
    fo(i,1,tx) d[i]+=d[i-1];
    fd(i,n,1) e[d[a[c[i]].x]--]=c[i];
    fo(i,1,n) aa[i].x=a[e[i]].x,aa[i].y=a[e[i]].y;
    fo(i,1,n) a[i].x=aa[i].x,a[i].y=aa[i].y;
    l=0;
    fo(i,1,n){
        l++;
        if (i<n&&a[i].x!=a[i+1].x) l=0;
        if (i<n&&a[i].x==a[i+1].x){
            an1=an2=an3=0;
            qry(1,1,ty,a[i].y+1,a[i+1].y-1);
            t=an1;
            //t=qry1(1,1,ty,a[i].y+1,a[i+1].y-1);
            if (l<cntx[a[i].x]-l) r=l;else r=cntx[a[i].x]-l;
            //r=min(l,cntx[a[i].x]-l);
            if (t<=r){
                if (t>ans1){
                    ans1=t;
                    ans2=an2;
                    ans3=(ll)an3*C(l,t)%mo*C(cntx[a[i].x]-l,t)%mo;
                }
                else if (t==ans1){
                    ans2=(ans2+an2)%mo;
                    ans3=(ans3+(ll)an3*C(l,t)%mo*C(cntx[a[i].x]-l,t)%mo)%mo;
                }
            }
        }
        change(1,1,ty,a[i].y);
    }
    clear();
    fo(i,1,n) cntx[a[i].x]--,cnty[a[i].y]--;
    fo(i,1,n) swap(a[i].x,a[i].y);
    swap(tx,ty);
    fo(i,1,n) cntx[a[i].x]++,cnty[a[i].y]++;
    //sort(a+1,a+n+1,cmp);
    fo(i,1,ty) d[i]=0; 
    fo(i,1,n) d[a[i].y]++;
    fo(i,1,ty) d[i]+=d[i-1];
    fd(i,n,1) c[d[a[i].y]--]=i;
    fo(i,1,tx) d[i]=0;
    fo(i,1,n) d[a[i].x]++;
    fo(i,1,tx) d[i]+=d[i-1];
    fd(i,n,1) e[d[a[c[i]].x]--]=c[i];
    fo(i,1,n) aa[i].x=a[e[i]].x,aa[i].y=a[e[i]].y;
    fo(i,1,n) a[i].x=aa[i].x,a[i].y=aa[i].y;
    l=0;
    fo(i,1,n){
        l++;
        if (i<n&&a[i].x!=a[i+1].x) l=0;
        if (i<n&&a[i].x==a[i+1].x){
            an1=an2=an3=0;
            qry(1,1,ty,a[i].y+1,a[i+1].y-1);
            t=an1;
            //t=qry1(1,1,ty,a[i].y+1,a[i+1].y-1);
            if (l<cntx[a[i].x]-l) r=l;else r=cntx[a[i].x]-l;
            //r=min(l,cntx[a[i].x]-l);
            if (t<r){
                if (t>ans1){
                    ans1=t;
                    ans2=an2;
                    ans3=(ll)an3*C(l,t)%mo*C(cntx[a[i].x]-l,t)%mo;
                }
                else if (t==ans1){
                    ans2=(ans2+an2)%mo;
                    ans3=(ans3+(ll)an3*C(l,t)%mo*C(cntx[a[i].x]-l,t)%mo)%mo;
                }
            }
        }
        change(1,1,ty,a[i].y);
    }
    l=0;
    fo(i,1,n) cnt[i]=0;
    fo(i,1,n){
        l++;
        t=min(l-1,min(cnt[a[i].y],min(cnty[a[i].y]-cnt[a[i].y]-1,cntx[a[i].x]-l)));
        if (t>ans1){
            ans1=t;
            ans2=1;
            ans3=(ll)C(l-1,t)%mo*C(cnt[a[i].y],t)%mo*C(cnty[a[i].y]-cnt[a[i].y]-1,t)%mo*C(cntx[a[i].x]-l,t)%mo;
        }
        else if (t==ans1){
            ans2=(ans2+1)%mo;
            ans3=(ans3+(ll)C(l-1,t)%mo*C(cnt[a[i].y],t)%mo*C(cnty[a[i].y]-cnt[a[i].y]-1,t)%mo*C(cntx[a[i].x]-l,t)%mo)%mo;
        }
        cnt[a[i].y]++;
        if (i<n&&a[i].x!=a[i+1].x) l=0;
    }
    printf("%d",ans1);
    if (czy>=2) printf(" %d",ans2);
    if (czy==3) printf(" %d",ans3);
    printf("\n");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值