zoukankan      html  css  js  c++  java
  • rectangle

    我颓代码了,我不是人

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

    $n<=10000,m<=2500$

    题解

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

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

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

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

     数字是枚举顺序

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

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

    之间每个点都会与下面点形成新的矩形,那么这样贡献就是$sumlimits_{ynow}^{y1<=ynow<=y2}$  $sumlimits_{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
    ",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
    ",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
    ",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
    ",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
    ",ans);
    }
    View Code
  • 相关阅读:
    防火墙(Iptables NAT)
    zookeeper-分布式协调技术的搭建
    Cobbler 自动安装 配置
    GPG非对称加密
    服务管理--Nginx
    NTP时间服务器
    Javascript-数据类型、类型转换
    移动端计算页面尺寸
    javascript 给关键字加链接
    JS三元运算符
  • 原文地址:https://www.cnblogs.com/znsbc-13/p/11619383.html
Copyright © 2011-2022 走看看