HDU 6356 Glad You Came 區間操作分解,倍增
阿新 • • 發佈:2018-11-02
剛開題面 式子有點多...不要慌..只是生成操作的資料.(防止因為讀入超時.)
題意:長度為n的序列a,初始為0, m次操作(l[i],r[i],v[i]) j=[l[i],r[i]] 若a[j]<v[i] 則令a[j]=v[i].
n<=1e5,m<=5e6. 輸出m次操作後,序列a的異或和.
若兩個操作的區間都相同,顯然其作用的只有v值較大的那一個.
另d=log2(r-l+1).則可以將一個詢問拆成兩個(l,r,v) -> (l,l+2^d-1),(r-2^d,r).
現在所有操作的區間長度都是2的冪次.
繼續將操作區間分解,長度從大到小不斷往下拆,例如長度為2^i分解成兩個2^i-1區間.直到分解到2^0即可.O(m+nlogn).
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned int ui; const int N=1e5+5,M=5e6+5; int T,n,m,lg[N]; ui a[20][N]; ui f[M*3]; ui x,y,z; ui rng(){ 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(){ scanf("%d",&T); lg[2]=1; for(int i=3;i<N;i++) lg[i]=lg[i>>1]+1; while(T--){ memset(a,0,sizeof(a)); scanf("%d%d%u%u%u",&n,&m,&x,&y,&z); for(int i=1;i<=3*m;i++) f[i]=rng(); int mx=0; for(int i=1;i<=m;i++){ int l=min(f[3*i-2]%n,f[3*i-1]%n)+1; int r=max(f[3*i-2]%n,f[3*i-1]%n)+1; ui v=f[3*i]%(1<<30); int d=lg[r-l+1]; a[d][l]=max(a[d][l],v); a[d][r-(1<<d)+1]=max(a[d][r-(1<<d)+1],v); mx=max(d,mx); } for(int k=mx;k>=1;k--){ for(int i=1;i<=n;i++){ if(i+(1<<k)-1>n) break; int d=1<<(k-1); a[k-1][i]=max(a[k-1][i],a[k][i]); a[k-1][i+d]=max(a[k-1][i+d],a[k][i]); } } ll res=0; for(int i=1;i<=n;i++) res^=1ll*a[0][i]*i; cout<<res<<'\n'; } return 0; }
線段樹維護區間的最小值.若該區間最小值<v[i] 則到該區間內暴力更新.也能抖過..