【JZOJ 5295】【清华集训2017模拟】Create

Description

这里写图片描述

Solution

题目有一个比较显然的性质:因为我们不停的把连续的一段变成一个数的话,那么全局不同的数会越来越少;换句话说,就是如果我们在区间修改时,对于一个数都相同的区间我们才能修改,这样均摊下来复杂度并不是很高,

所以,现在的问题就变成了:如何快速求一段数相同的区间内对答案的贡献,

设这个区间的值为c,那么只有v小于等于c的三元组才有可能有贡献,
而贡献为多少,则是这个区间被这些可能的三元组覆盖的总面积(这么写大家看得懂吧。。。),

这个是标准的主席树嘛,

这样的复杂度为 O(nlog(n)2) ,可能过不了,
我们发现,在主席树上遍历的过程和在线段树上的很像,考虑两个同时进行,这样既可。

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define min(q,w) ((q)>(w)?(w):(q))
#define max(q,w) ((q)<(w)?(w):(q))
#define CNT(L,R,E) (finds(1,n,root[fef[E]],L,R))
#define NW(e1,e) if(!(e)||(e1)==(e))b[(e)=++b0]=b[e1];
using namespace std;
typedef long long LL;
const int N=100500;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n;
LL ans;
int a[N];
struct qqww
{
    int v,la,s;
}b1[N*4];
int root[N],b0;
struct qwqw
{
    int l,r,v;
    int la;
}b[N*70];
int sc[N][3],p[N],pn;
int fef[N];
bool PX(int q,int w){return sc[q][2]<sc[w][2];}
void build(int l,int r,int e1,int e,int l1,int r1)
{
    if(l1<=l&&r<=r1)
    {
        b[e].la++;
        b[e].v+=(r-l+1);
        return;
    }
    b[e].v+=(r1-l1+1);
    int t=(l+r)>>1;
    if(r1<=t)
    {
        NW(b[e1].l,b[e].l);
        build(l,t,b[e1].l,b[e].l,l1,r1);
    }
    else if(t<l1)
    {
        NW(b[e1].r,b[e].r);
        build(t+1,r,b[e1].r,b[e].r,l1,r1);
    }
    else 
    {
        NW(b[e1].l,b[e].l);NW(b[e1].r,b[e].r);
        build(l,t,b[e1].l,b[e].l,l1,t),build(t+1,r,b[e1].r,b[e].r,t+1,r1);
    }
}
int finds(int l,int r,int e,int l1,int r1)
{
    if(!e)return 0;
    if(l1==l&&r1==r)return b[e].v;
    int t=(l+r)>>1;
    int ans=(r1-l1+1)*b[e].la;
    if(r1<=t)ans+=finds(l,t,b[e].l,l1,r1);
    else if(t<l1)ans+=finds(t+1,r,b[e].r,l1,r1);
    else ans+=finds(l,t,b[e].l,l1,t)+finds(t+1,r,b[e].r,t+1,r1);
    return ans;
}
void merge(int l,int r,int e)
{
    if(b1[e*2].v==b1[e*2+1].v&&b1[e*2].v!=-1)
    {
        b1[e].v=b1[e*2].v;
        int t=(l+r)>>1;
        if(b1[e*2].s==-1)b1[e*2].s=CNT(l,t,b1[e].v);
        if(b1[e*2+1].s==-1)b1[e*2+1].s=CNT(t+1,r,b1[e].v);
        b1[e].s=b1[e*2].s+b1[e*2+1].s;
    }else b1[e].v=-1,b1[e].s=-1;
}
void build(int l,int r,int e)
{
    if(l==r)
    {
        b1[e].v=a[l];
        ans+=(b1[e].s=CNT(l,r,a[l]));
        return;
    }
    int t=(l+r)>>1;
    build(l,t,e*2);
    build(t+1,r,e*2+1);
    merge(l,r,e);
}
inline void doit(int l,int r,int e)
{
    if(!b1[e].la)return;
    b1[e].v=b1[e].la;
    b1[e].s=-1;
    if(l!=r)b1[e*2].la=b1[e].la,b1[e*2+1].la=b1[e].la;
    b1[e].la=0;
}
void change(int l,int r,int e,int l1,int r1,int l2)
{
    int t=(l+r)>>1;
    if(l!=r)doit(l,t,e*2),doit(t+1,r,e*2+1);
    if(l1<=l&&r<=r1)
    {
        if(b1[e].v!=-1)
        {
            if(b1[e].s==-1)b1[e].s=CNT(l,r,b1[e].v);
            ans-=b1[e].s;
            b1[e].la=l2;
            doit(l,r,e);
            b1[e].s=CNT(l,r,b1[e].v);
            ans+=b1[e].s;
            return;
        }
    }
    if(r1<=t)change(l,t,e*2,l1,r1,l2);
    else if(t<l1)change(t+1,r,e*2+1,l1,r1,l2);
    else change(l,t,e*2,l1,t,l2),change(t+1,r,e*2+1,t+1,r1,l2);
    merge(l,r,e);
}
int main()
{
    freopen("create.in","r",stdin);
    freopen("create.out","w",stdout);
    int q,w,e,_;
    read(n),read(m),read(_);
    fo(i,1,n)read(a[i]);
    fo(i,1,m)
    {
        p[i]=i;
        fo(j,0,2)read(sc[i][j]);
    }
    sort(p+1,p+1+m,PX);
    sc[0][2]=-2333;
    w=0;
    fo(i,1,m)
    {
        w+=(sc[q][2]!=sc[p[i]][2]);
        q=p[i];
        NW(root[w-1],root[w])
        build(1,n,root[w-1],root[w],sc[q][0],sc[q][1]);
        p[w]=sc[q][2];
    }
    pn=w;
    fo(i,1,pn)fef[p[i]]=i;
    fo(i,1,n+1)if(!fef[i])fef[i]=fef[i-1];
    ans=0;
    build(1,n,1);
    printf("%d\n",ans);
    fo(i,1,_)
    {
        read(q),read(w),read(e);
        q^=ans;w^=ans;e^=ans;
        change(1,n,1,q,w,e);
        printf("%lld\n",ans);
    }
    fclose(stdout);
    fclose(stdin);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值