rectangle

Posted znsbc-13

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

 

以上是关于rectangle的主要内容,如果未能解决你的问题,请参考以下文章