1. 程式人生 > >HDU 6356 Glad You Came(st表/線段樹/單調佇列)

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)$

其實就是把詢問的區間都分成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);

	}
}