HDU 6356 Glad You Came(线段树)

Description

给出一个长度为 n n 的序列a1,...,an,初始全是 0 0 ,有m次操作,第 i i 次操作将区间[li,ri]内所有数字与 vi v i 取较大值,最后求 i=1niai ∑ i = 1 n i ⊕ a i ,其中 为异或操作

Input

首先输入一整数 T T 表示用例组数,对于每组用例输入五个整数n,m,X,Y,Z,其中 X,Y,Z X , Y , Z 用于生成 li,ri,vi l i , r i , v i

(1T100,1n105,1m5106,0X,Y,Z<230) ( 1 ≤ T ≤ 100 , 1 ≤ n ≤ 10 5 , 1 ≤ m ≤ 5 ⋅ 10 6 , 0 ≤ X , Y , Z < 2 30 )

Output

输出 i=1niai ∑ i = 1 n i ⊕ a i

Sample Input

4
1 10 100 1000 10000
10 100 1000 10000 100000
100 1000 10000 100000 1000000
1000 10000 100000 1000000 10000000

Sample Output

1031463378
1446334207
351511856
47320301347

Solution

每次操作 l r v l   r   v 即为将 ai a i 变成 max(ai,v),lir m a x ( a i , v ) , l ≤ i ≤ r ,用线段树维护区间最小值 Min[t] M i n [ t ] ,该区间的标记 Tag[t] T a g [ t ] 以及不小于该区间标记的数字个数 Num[t] N u m [ t ] ,如果当前更新值 v v 不超过之前该区间的Tag值,那么当前操作无效,否则清空该区间所有小于 v v 的数字,然后区间更新为v,其中 Num N u m 数组的作用主要是辅助维护区间最小值,因为只有区间有部分被清空时才会存在小于 Tag T a g 的值,也即 Num[t]<rl+1 N u m [ t ] < r − l + 1 时,会有数字需要被变成 v v ,此时区间最小值是v,否则该部分不需要更新,区间最小值不变

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define INF (1<<30)
#define maxn 100005
#define ls (t<<1)
#define rs ((t<<1)|1)
int Num[maxn<<2],Min[maxn<<2],Tag[maxn<<2];
int T,n,m;
void push_up(int t)
{
    Min[t]=min(Min[ls],Min[rs]);
    Num[t]=Num[ls]+Num[rs];
}
void build(int l,int r,int t)
{
    Num[t]=Min[t]=Tag[t]=0;
    if(l==r)
    {
        Min[t]=Tag[t]=0,Num[t]=1;
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,ls),build(mid+1,r,rs);
    push_up(t);
}
void modify(int l,int r,int t,int v)
{
    if(Tag[t]>=v)return ;
    Tag[t]=v;
    if(Num[t]<r-l+1)
    {
        Min[t]=v;
        Num[t]=r-l+1;
    }
}
void push_down(int l,int r,int t)
{
    if(!Tag[t])return ;
    int mid=(l+r)/2;
    modify(l,mid,ls,Tag[t]),modify(mid+1,r,rs,Tag[t]);
}
void clear(int l,int r,int t,int v)
{
    if(Min[t]>=v)return ;
    Tag[t]=0;
    if(l==r)
    {
        Min[t]=Num[t]=0;
        return ;
    }
    int mid=(l+r)/2;
    clear(l,mid,ls,v),clear(mid+1,r,rs,v);
    push_up(t);
}
void update(int L,int R,int l,int r,int t,int v)
{
    if(Min[t]>=v)return ;
    if(L<=l&&r<=R)
    {
        clear(l,r,t,v);
        modify(l,r,t,v);
        return ;
    }
    push_down(l,r,t);
    int mid=(l+r)/2;
    if(L<=mid)update(L,R,l,mid,ls,v);
    if(R>mid)update(L,R,mid+1,r,rs,v);
    push_up(t);
}
int query(int L,int R,int l,int r,int t)
{
    if(L<=l&&r<=R)return Min[t];
    int ans=INF;
    push_down(l,r,t);
    int mid=(l+r)/2;
    if(L<=mid)ans=min(ans,query(L,R,l,mid,ls));
    if(R>mid)ans=min(ans,query(L,R,mid+1,r,rs));
    return ans;
}
unsigned X,Y,Z;
unsigned F()
{
    X^=(X<<11);
    X^=(X>>4);
    X^=(X<<5);
    X^=(X>>14);
    unsigned W=X^(Y^Z);
    X=Y;
    Y=Z;
    Z=W;
    return Z;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%u%u%u",&n,&m,&X,&Y,&Z);
        build(1,n,1);
        int mod=1<<30;
        for(int i=1;i<=m;i++)
        {
            unsigned f1,f2,f3;
            f1=F();
            f2=F();
            f3=F();
            int L=min(f1%n+1,f2%n+1),R=max(f1%n+1,f2%n+1),v=f3%mod;
            update(L,R,1,n,1,v);
        }
        ll ans=0;
        for(int i=1;i<=n;i++)ans^=((ll)i*query(i,i,1,n,1));
        printf("%lld\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值