rectangle

我颓代码了,我不是人

要是不颓代码我绝对不会想到树状数组可以这么搞

 

$n<=10000,m<=2500$

题解

$2^n$算法,枚举子集

$n^4$算法,枚举四个点,这样绝对不重不漏

$n^2*log$算法,一层循环枚举$l$这一列,一层循环枚举$r$,这一列

考虑如何计算卡在$l,r$之间的值,考虑枚举上边界(这里所说的上边界就是$l$对应列,$r$对应列上的所有点,按照$y$排序后从小到大枚举点)

 

 数字是枚举顺序

考虑上边界和下边界之间贡献

设上边界$y1$,下边界$y2$,之间点个数为$cnt$,下面有$w$个点

之间每个点都会与下面点形成新的矩形,那么这样贡献就是$\sum\limits_{ynow}^{y1<=ynow<=y2}$  $\sum\limits_{ypre}^{ypre<y1} (ynow -ypre)*(r-l)$(之前每个矩形都可以扩大这些)

那么现在我们要快速查$ynow$-$ypre$不同值的和

树状数组即可

这里,树状数组实现很$sb$,然后求出来这之间$y$之和,再$-$下面所有$y$下标,这样得到了真实长度,

给一下实现,$ask2$是求和,$ask1$是求个数

 1 (ans+=((ask2(nxt-1)-ask2(cur-1))*ask1(down)%mod-((ask1(nxt-1)-ask1(cur-1)))*ask2(down)%mod)*(i-j))%mod; 

$(ask2(nxt-1)-ask2(cur-1))*ask1(down)$是求出来这一段下标之和,每一个下面的点都会有$ask2(nxt-1)-ask2(cur-1)$贡献,$(ask1(nxt-1)-ask1(cur-1)))*ask2(down)$是求出来下面下标和,每一个点下面下标和即为$ask2$

大致长这样

这里实现还有一些小注意点,

1.枚举$r$然后你从大到小枚举$l$这样你每次树状数组不用再清空可以重复利用上次值

2.枚举上边界可以单调指针扫

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 2510
#define fi first
#define se second
#define m 2500
const ll mod=1e9+7;
ll f[A][A],cnt[A],sum[A],vec[A][A],c[A],vis[A];
ll ans,n;
ll ask1(ll x){
    ll ans=0;
    for(ll i=x;i>=1;i-=i&-i)
        ans+=cnt[i];
    return ans;
}
ll ask2(ll x){
    ll ans=0;
    for(ll i=x;i>=1;i-=i&-i)
        ans+=sum[i];
    return ans;
}
//1个数,2总和
void add1(ll x,ll d){
    for(ll i=x;i<=m;i+=i&-i)
        cnt[i]+=d;
}
void add2(ll x,ll d){
    for(ll i=x;i<=m;i+=i&-i)
        sum[i]+=d;
}
void update(ll x){
    if(vis[x]) return ;
    //printf("i=%lld\n",x),
    vis[x]=1;
    add1(x,1);add2(x,x);
}
void init(){
    memset(sum,0,sizeof(sum));
    memset(cnt,0,sizeof(cnt));
    memset(vis,0,sizeof(vis));
}
int main(){
//    freopen("da.in","r",stdin);
//    freopen("ans.bf","w",stdout);
    scanf("%lld",&n);
    for(ll i=1,x,y;i<=n;i++){
        scanf("%lld%lld",&x,&y);
        vec[x][++c[x]]=y;
    }
    
    for(ll i=1;i<=m;i++){
        sort(vec[i]+1,vec[i]+c[i]+1);
        vec[i][c[i]+1]=m+1;
    }
    for(ll i=1;i<=m;i++){//枚举上边界,然后更新数组
        printf("%lld\n",i);
        if(c[i]){
            init();
            for(ll j=1;j<=c[i];j++) update(vec[i][j]);
            for(ll j=i-1;j>=1;j--){
//                printf("j=%lld\n",j);
                if(c[j]){
                    ll ita=1,itb=1,cur=max(vec[i][ita],vec[j][itb]);
                    for(ll k=1;k<=c[j];k++) update(vec[j][k]);
//                    printf("ita=%lld itb=%lld cur=%lld\n",ita,itb,cur);
                    while(vec[i][ita+1]<=cur) ita++;
                    while(vec[j][itb+1]<=cur) itb++;
                    while(ita<=c[i]&&itb<=c[j]){
                        ll nxt=min(vec[i][ita+1],vec[j][itb+1]),down=min(vec[i][ita],vec[j][itb]);//上边界
                        (ans+=((ask2(nxt-1)-ask2(cur-1))*ask1(down)%mod-((ask1(nxt-1)-ask1(cur-1)))*ask2(down)%mod)*(i-j))%mod;
                        //sb容斥
                        cur=nxt;
                        if(vec[i][ita+1]<=cur) ita++;
                        if(vec[j][itb+1]<=cur) itb++;
                    }
                }
            }
        }
    }
    printf("%lld\n",ans);
}
View Code

 

posted @ 2019-10-03 11:46  znsbc  阅读(996)  评论(0编辑  收藏  举报