HDU 6356 Glad You Came(st表/线段树/单调队列)

题目链接
题意:

Let the i-th result value of calling the above function as f_i(i = 1, 2, \cdots, 3 m) The i-th operation of Steve is to update aj as vi if a_j < v_i (j = l_i, l_i + 1, \cdots, r_i), where

\begin{cases} l_i &= \min\left((f_{3 i - 2} \bmod n) + 1, (f_{3 i - 1} \bmod n) + 1\right) \\ r_i &= \max\left((f_{3 i - 2} \bmod n) + 1, (f_{3 i - 1} \bmod n) + 1\right) \\ v_i &= f_{3 i} \bmod 2^{30}\end{cases} (i = 1, 2, \cdots, m).

一开始给你一个序列a,里面全是0,然后有m次操作,每次操作给你[l,r],v,更新[l,r]区间,将所有区间内小于v的元素都改成v

每次操作的变量由上面那个式子产生,里面的f是由上面那个随机数函数产生

解析:

这道题......m这么大,连排序都不能排.......竟然能用最裸的在线更新的线段树过..........这也太坑了把..........

官方题解好像使用st表来做的

如果有两个操作覆盖相同的区间,我们可以保留最大的那个。对于每个操作(l,r,v),令d等于⌊log​2​​(r−l+1)⌋⌊log​2​​(r−l+1)⌋

我们可以用两个操作(l, l + 2^d - 1, v)(r - 2^d + 1, r, v)替换此操作。这样做之后,每个操作所覆盖的区间长度均为 2 的幂,这意味着长度仅有$\mathcal{O}(\log n)$ 种。剩下的只不过是,按长度递减的顺序枚举操作,将每个操作分成两个相等长度的操作,直到区间长度为一。这样做的时间复杂度为 $\mathcal{O}(m + n \log n)$,空间复杂度为$\mathcal{O}(n \log n)$

扫描二维码关注公众号,回复: 2704207 查看本文章

其实就是把询问的区间都分成2的幂次的长度,这样长度最多只有\log n种,然后再用逆的st表做就可以了

st表详解

st表好像要3.2s。。。。

#include <bits/stdc++.h>
using namespace std;
typedef long long  ll;
typedef unsigned int ui;
#define pb push_back
const int MAXN = 1e5+10;
const int MAXM = 5e6+10;
const ll INF = 0x3f3f3f3f;

ui X,Y,Z;
ui f[4*MAXM];
int a[MAXN];

typedef struct interval
{
    int L;
    int v;
}interval;

int mq[50][MAXN];
int two[50];
interval hq[MAXN];
int Log[MAXN];

inline ui RNG61()
{
    X=X^(X<<11);
    X=X^(X>>4);
    X=X^(X<<5);
    X=X^(X>>14);
    ui W=X^(Y^Z);
    X=Y;
    Y=Z;
    Z=W;
    return Z;
}


int main()
{
    int t;
    int kk=1<<30;
    scanf("%d",&t);
    int tmp=1;
    Log[1]=0;
    Log[2]=1;
    for(int i=0;i<=20;i++) two[i]=tmp,tmp=tmp<<1;
    for(int i=3;i<MAXN;i++) Log[i]=Log[i>>1]+1;   //log2预处理
    while(t--)
    {

        ll n,m;

        scanf("%lld%lld%u%u%u",&n,&m,&X,&Y,&Z);
        int kkk=Log[n];
        for(int i=0;i<=n;i++) a[i]=0;
        for(int i=0;i<=kkk;i++)
            for(int j=1;j<=n;j++)
                mq[i][j]=0;
        for(int i=1;i<=3*m;i++)
        {
            f[i]=RNG61();
        }

        for(int i=1;i<=m;i++)
        {
            int l=min((f[3*i-2]%n)+1,(f[3*i-1]%n)+1);
            int r=max((f[3*i-2]%n)+1,(f[3*i-1]%n)+1);
            int v=f[3*i]%kk;
            int d=Log[r-l+1];
            mq[d][l]=mq[d][l]<v?v:mq[d][l];
            mq[d][r-two[d]+1]=mq[d][r-two[d]+1]<v?v:mq[d][r-two[d]+1];

        }
        for(int i=kkk;i>0;i--)
        {
            for(int j=1;j<=n;j++)
            {
                if(j+two[i-1]>n) break;
                mq[i-1][j]=max(mq[i-1][j],mq[i][j]);
                mq[i-1][j+two[i-1]]=max(mq[i-1][j+two[i-1]],mq[i][j]);
            }

        }
        ll ans=0;
        for(ll i=1;i<=n;i++)
        {
            ans=ans^(i*mq[0][i]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

我一开始是用单调队列代替st表的,然后必须把log进行预处理,常数优化才能过....3.3s

#include <bits/stdc++.h>
using namespace std;
typedef long long  ll;
typedef unsigned int ui;
#define pb push_back
const int MAXN = 1e5+10;
const int MAXM = 5e6+10;
const ll INF = 0x3f3f3f3f;

ui X,Y,Z;
ui f[4*MAXM];
int a[MAXN];

typedef struct interval
{
    int L;
    int v;
}interval;

int mq[50][MAXN];
int two[50];
interval hq[MAXN];
int Log[MAXN];

inline ui RNG61()
{
    X=X^(X<<11);
    X=X^(X>>4);
    X=X^(X<<5);
    X=X^(X>>14);
    ui W=X^(Y^Z);
    X=Y;
    Y=Z;
    Z=W;
    return Z;
}


int main()
{
    int t;
    int kk=1<<30;
    scanf("%d",&t);
    int tmp=1;
    Log[1]=0;
    Log[2]=1;
    for(int i=0;i<=20;i++) two[i]=tmp,tmp=tmp<<1;
    for(int i=3;i<MAXN;i++) Log[i]=Log[i>>1]+1;   //log2预处理
    while(t--)
    {

        ll n,m;

        scanf("%lld%lld%u%u%u",&n,&m,&X,&Y,&Z);
        int kkk=Log[n];
        for(int i=0;i<=n;i++) a[i]=0;
        for(int i=0;i<=kkk;i++)
            for(int j=1;j<=n;j++)
                mq[i][j]=0;
        for(int i=1;i<=3*m;i++)
        {
            f[i]=RNG61();
        }

        for(int i=1;i<=m;i++)
        {
            int l=min((f[3*i-2]%n)+1,(f[3*i-1]%n)+1);
            int r=max((f[3*i-2]%n)+1,(f[3*i-1]%n)+1);
            int v=f[3*i]%kk;
            int d=Log[r-l+1];
            mq[d][l]=mq[d][l]<v?v:mq[d][l];
            mq[d][r-two[d]+1]=mq[d][r-two[d]+1]<v?v:mq[d][r-two[d]+1];

        }

        int head,tail;
        head=tail=0;
        for(int i=kkk;i>=0;i--)
        {
            head=tail=0;
            for(int j=1;j<=n;j++)
            {
                while(head<tail&&hq[tail-1].v<=mq[i][j])
                    tail--;
                hq[tail++]=interval{j,mq[i][j]};
                if(head<tail)
                    a[j]=max(hq[head].v,a[j]);
                if(j-two[i]+1>=1)
                {
                    while(head<tail&&hq[head].L<=j-two[i]+1)
                        head++;
                }
            }

        }
        ll ans=0;
        for(ll i=1;i<=n;i++)
        {
            ans=ans^(i*a[i]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

最裸的线段树,2.9s.......这个是最快的

#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
#define lch root<<1
#define rch root<<1|1
typedef unsigned int ui;
typedef long long ll;
const int MAXN = 1e5+10;
const int MAXM = 5e6+10;
const ll INF = 0x3f3f3f3f;

ui X,Y,Z;
ui f[4*MAXM];
int a[MAXN];
int Btree[MAXN*4];

inline ui RNG61()
{
    X=X^(X<<11);
    X=X^(X>>4);
    X=X^(X<<5);
    X=X^(X>>14);
    ui W=X^(Y^Z);
    X=Y;
    Y=Z;
    Z=W;
    return Z;
}

void updateone(int root,int s1,int e1,int l,int r,int val)  //维护区间最小值
{
    if(e1<l||s1>r) return;
    if(s1>e1)return;
	if(Btree[root]>val) return;
    if(s1==e1)
    {
        Btree[root]=val;
        a[s1]=val;
        return;
    }
    //pushDown(root);
    int mid=(s1+e1)>>1;
    if(l<=mid)
        updateone(root<<1,s1,mid,l,r,val);
    if(mid+1<=r)
        updateone((root<<1)|1,mid+1,e1,l,r,val);
	Btree[root]=min(Btree[lch],Btree[rch]);
    //push_up(root);
}


int main()
{
    int t;
    int kk=1<<30;
    scanf("%d",&t);
    while(t--)
    {

        ll n,m;

        scanf("%lld%lld%u%u%u",&n,&m,&X,&Y,&Z);
        for(int i=0;i<=n;i++) a[i]=0;
		memset(Btree,0,sizeof(Btree));
        for(int i=1;i<=3*m;i++)
        {
            f[i]=RNG61();
        }

		for(int i=1;i<=m;i++)
        {
            int l=min((f[3*i-2]%n)+1,(f[3*i-1]%n)+1);
            int r=max((f[3*i-2]%n)+1,(f[3*i-1]%n)+1);
            int v=f[3*i]%kk;
            
			updateone(1,1,n,l,r,v);

        }
		ll ans=0;
		for(ll i=1;i<=n;i++)
        {
            ans=ans^(i*a[i]);
        }
		printf("%lld\n",ans);

	}
}

猜你喜欢

转载自blog.csdn.net/qq_37025443/article/details/81483083