JZOJ5295. 【清华集训2017模拟】Create

6 篇文章 0 订阅
2 篇文章 0 订阅

题意

数据范围

Analysis

真是一道非常恶心的题目。。。。首先考虑没变化之前的答案怎么求?可以发现,每一个位置的 Ai A i ,只对 x x 小于它的询问有贡献,而贡献不变。所以将询问按x排序,算答案,我们可以求出该位置被多少个询问覆盖,考虑对排序后的询问建立主席树,每次二分找到位置,然后区间查询即可得到答案。再考虑修改?每次修改一段区间,考虑修改多次以后,整个数组必定是由一段一段相同的数组成的。那么我们可以用 set s e t 维护相同的一段数的区间,每一次修改就把 l,r l , r 所在的区间分裂,然后减去 l,r l , r 中间区间的贡献,注意两端的贡献也要减去。最后将 l,r l , r 的贡献加上,并加入一个新的区间 l,r l , r 。这样每次仅至多会生成3个区间,而每个区间至多被删掉一次。复杂度为 O((m+q)logn) O ( ( m + q ) l o g n )

Code

# include<cstdio>
# include<cstring>
# include<algorithm>
# include<set>
using namespace std;
const int N = 1e5 + 5;
struct node
{
    int l,r,x;
}q[N];
typedef long long ll;
int ls[N * 80],rs[N * 80],rt[N],c[N];
ll sum[N * 80],tag[N * 80];
int n,m,t,tot;
ll ans;
set <int> s;
set <int> ::iterator it;
bool cmp(node a,node b) { return a.x < b.x; }
inline void ins(int &x,int y,int l,int r,int l1,int r1)
{
    x = ++tot;
    ls[x] = ls[y],rs[x] = rs[y],tag[x] = tag[y];
    if (l >= l1 && r <= r1) { ++tag[x],sum[x] = sum[y] + r - l + 1; return; }
    int mid = (l + r) >> 1;
    if (l1 <= mid) ins(ls[x],ls[y],l,mid,l1,r1);
    if (r1 > mid) ins(rs[x],rs[y],mid + 1,r,l1,r1);
    sum[x] = sum[ls[x]] + sum[rs[x]] + (r - l + 1) * tag[x];
}
inline ll qry(int &x,int l,int r,int l1,int r1,ll v)
{
    if (!x) x = ++tot;
    if (l >= l1 && r <= r1) return v * (r - l + 1) + sum[x];
    int mid = (l + r) >> 1; ll ret = 0; v += tag[x];
    if (l1 <= mid) ret = qry(ls[x],l,mid,l1,r1,v);
    if (r1 > mid) ret += qry(rs[x],mid + 1,r,l1,r1,v);
    return ret;
}
inline int find(int x)
{
    int l = 1,r = m,ret = 0;
    while (l <= r)
    {
        int mid = (l + r) >> 1;
        if (q[mid].x <= x) l = mid + 1,ret = mid;
        else r = mid - 1;
    }
    return ret;
}
int main()
{
    freopen("create.in","r",stdin);
    freopen("create.out","w",stdout);
    scanf("%d%d%d",&n,&m,&t);
    for (int i = 1 ; i <= n ; ++i) scanf("%d",&c[i]);
    for (int i = 1 ; i <= m ; ++i) scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].x);
    sort(q + 1,q + m + 1,cmp);
    for (int i = 1 ; i <= m ; ++i) ins(rt[i],rt[i - 1],1,n,q[i].l,q[i].r);
    s.insert(n + 1),s.insert(0); int las = 1,p = 1;
    for (; las <= n ; las = p + 1,p = las) 
    { 
        for (; p <= n && c[p] == c[p + 1] ; ++p);
        ans += qry(rt[find(c[p])],1,n,las,p,0ll),s.insert(las);
    }
    printf("%lld\n",ans);
    while (t--)
    {
        int l,r,v; scanf("%d%d%d",&l,&r,&v);
        l ^= ans,r ^= ans,v ^= ans;
        las = l; int val = c[*(--s.upper_bound(l))],pos;
        if (r + 1 <= n && !s.count(r + 1)) pos = *(--s.upper_bound(r)),c[r + 1] = c[pos],s.insert(r + 1);
        while (las <= r)
        {
            pos = *s.upper_bound(las);
            ans -= qry(rt[find(val)],1,n,las,min(r,pos - 1),0ll);
            val = c[pos],las = pos;
            if (pos <= r) s.erase(pos);
        }
        s.insert(l),c[l] = v;
        ans += qry(rt[find(v)],1,n,l,r,0ll);
        printf("%lld\n",ans);
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值