【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;
}
posted @ 2018-04-11 21:19  Maxwei_wzj  阅读(114)  评论(0编辑  收藏  举报