zoukankan      html  css  js  c++  java
  • 【51Nod1688】LYKMUL-线段树+乘法原理

    测试地址:LYKMUL
    做法:本题需要用到线段树+乘法原理。
    这题是清北学堂zhw出的,当时讲的时候觉得非常妙,于是写在这里。
    可以看出,一个集合的贡献等于这个集合中区间交和并的乘积,它也可以写成:
    ab1
    我们考虑每对这样的(a,b)对答案的贡献,不难看出,贡献就是使得a在交集,同时b在并集的集合数量。我们怎么计算这个数量呢?
    考虑枚举a,并提取出覆盖a的区间,不难看出满足条件的集合只能从这些区间中选,再考虑此时的一个b,假设它被k个覆盖a的区间覆盖,总的覆盖a的区间数量是tot,我们知道,如果b在一个集合的并集中,那么必须在k个覆盖它的区间选择至少一个,这样的方案数是2k1,而剩下的区间就可以任意选了,方案数是2totk,根据乘法原理,(a,b)的总贡献就是2totk(2k1)=2tot(12k)。那么在a固定时,所有(a,b)的总贡献是2totb=12n(12k(b)),也就是2tot(2nb=12n2k(b))
    看到这个式子,很快想到用数据结构维护里面的和式,因为涉及的操作只有区间乘和区间求和,显然可以用线段树维护。那么我们只需要将区间的左右端点放在一起排个序,求出每个区间出现和消失的时间,在枚举a的同时求出总贡献和就行了,总的时间复杂度为O(nlogn)
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll mod=1000000007;
    const ll inv=500000004;
    int n,l[100010],r[100010],tot=0;
    ll seg[800010],p[800010],ans;
    struct oper
    {
        int id,type,pos;
    }q[200010];
    
    bool cmp(oper a,oper b)
    {
        return a.pos<b.pos;
    }
    
    void pushup(int no)
    {
        seg[no]=(seg[no<<1]+seg[no<<1|1])%mod;
    }
    
    void buildtree(int no,int l,int r)
    {
        p[no]=1;
        if (l==r) {seg[no]=1;return;}
        int mid=(l+r)>>1;
        buildtree(no<<1,l,mid);
        buildtree(no<<1|1,mid+1,r);
        pushup(no);
    }
    
    void pushdown(int no)
    {
        if (p[no]!=1)
        {
            p[no<<1]=(p[no<<1]*p[no])%mod;
            p[no<<1|1]=(p[no<<1|1]*p[no])%mod;
            seg[no<<1]=(seg[no<<1]*p[no])%mod;
            seg[no<<1|1]=(seg[no<<1|1]*p[no])%mod;
            p[no]=1;
        }
    }
    
    void modify(int no,int l,int r,int s,int t,ll x)
    {
        if (l>=s&&r<=t)
        {
            p[no]=(p[no]*x)%mod;
            seg[no]=(seg[no]*x)%mod;
            return;
        }
        int mid=(l+r)>>1;
        pushdown(no);
        if (s<=mid) modify(no<<1,l,mid,s,t,x);
        if (t>mid) modify(no<<1|1,mid+1,r,s,t,x);
        pushup(no);
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&l[i],&r[i]);
            q[++tot].id=i,q[tot].type=0,q[tot].pos=l[i];
            q[++tot].id=i,q[tot].type=1,q[tot].pos=r[i]+1;
        }
    
        n<<=1;
        buildtree(1,1,n);
        sort(q+1,q+n+1,cmp);
        int last=1;
        ans=0;
        ll now=1;
        for(int i=1;i<=n;i++)
        {
            while(last<=n&&q[last].pos<=i)
            {
                int p=q[last].id;
                if (!q[last].type) now=(now<<1)%mod,modify(1,1,n,l[p],r[p],inv);
                else now=(now*inv)%mod,modify(1,1,n,l[p],r[p],2);
                last++;
            }
            ans=((ans+now*(n-seg[1]))%mod+mod)%mod;
        }
        printf("%lld",ans);
    
        return 0;
    }
  • 相关阅读:
    ZooKeeper 相关知识
    zookeeper 启动和停止脚本
    es 6.4.3 版本的es的处理方式
    SpringBoot启动使用elasticsearch启动异常:Received message from unsupported version:[2.0.0] minimal compatible
    windows下安装elasticsearch-6.4.3和elasticsearch-head插件
    二项式公式
    计算公式
    大规模数据如何检索?
    设计数据服务:为报表服务提供服务接口
    win10 桌面快捷键技术
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793466.html
Copyright © 2011-2022 走看看