標籤:hdu5172 gtys gay friends 線段樹單點更新 bestcoder#29
題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=5172
題意:給出n個數,m個詢問,問你[l,r]區間內是否為1到r-l+1的全排列。 大小很容易我們通過記錄首碼和很容易求出來,但是關鍵是去重。 考慮線段樹做法,我們記錄每個點的靠左最近的相同元素的位置,然後求 整個區間的最大值(即最大的前驅)如果小於l,即滿足條件,輸出YES。
好吧,其實這個題目我是搜的RMQ演算法出來的,因為我想練一下RMQ演算法,所以我就看了一下別人的部落格,自己也寫了一下,結果死活MLE。。。
好吧,我仔細看了一下,1000000的資料量用RMQ開一個二維數組1000000*20,顯然會超記憶體。。。
結果我不得不老老實實的滾回去用線段樹寫了。。。orz....
RMQ的MLE代碼:
#include<iostream>#include<string>#include<cstdio>#include<cstring>#include<queue>#include<map>#include<cmath>#include<stack>#include<set>#include<vector>#include<algorithm>#define LL long long#define inf 1<<30#define sf(a) scanf("%d",&a);#define CLEAR(a,b) memset(a,b,sizeof(a))using namespace std;/* TLE...顯然超記憶體;*/const int N=1000005;int n,m,a,b;int num[N],pre[N];int dp[N][21];int sum[N];void ST(int len){ for(int i=1;i<=n;i++) dp[i][0]=num[i]; for(int j=1;1<<j < n;j++){ for(int i=1;i+(1<<j)-1<n;i++){ dp[i][j]=max(dp[i][j-1],dp[i+1<<(j-1)][j-1]); } }}int rmq(int s,int v){ int k=(int)(log(v-s+1)*1.0/log(2.0)); return max(dp[s][k],dp[v-1<<k+1][k]);}int main(){ while(~scanf("%d%d",&n,&m)){ CLEAR(pre,0); CLEAR(sum,0); for(int i=1;i<=n;i++){ sf(a); sum[i]+=sum[i-1]+a; num[i]=pre[a]; pre[a]=i; } ST(n); while(m--){ sf(a);sf(b); double tmp=(b-a+2)*(b-a+1)*1.0/2.0; if(tmp!=sum[b]-sum[a-1]){ //cout<<tmp<<' '<<sum[b]-sum[a-1]<<' '; printf("NO\n"); continue; }else{ if(rmq(a,b)<a) printf("YES\n"); else printf("NO\n"); } } } return 0;}
線段樹AC代碼:
#include<iostream>#include<string>#include<cstdio>#include<cstring>#include<queue>#include<map>#include<cmath>#include<stack>#include<set>#include<vector>#include<algorithm>#define LL long long#define inf 1<<30#define s(a) scanf("%d",&a)#define CLEAR(a,b) memset(a,b,sizeof(a))using namespace std;const int N=1000005;int n,m,a,b;int pre[N],sum[N]; // sum存總和,pre定位該數字上一次出現的位置;struct node{ int l,r; int pre; // 用線段樹維護最近一個重複的數字;}node[N<<2];void PushUp(int rt){ node[rt].pre=max(node[rt<<1].pre,node[rt<<1|1].pre);}void build(int l,int r,int rt){ node[rt].l=l; node[rt].r=r; node[rt].pre=0; if(l!=r){ int mid=(l+r)>>1; build(l,mid,rt<<1); build(mid+1,r,rt<<1|1); }}void Insert(int v,int p,int rt){ int ll=node[rt].l,rr=node[rt].r; if(ll==rr&&p==ll){ node[rt].pre=pre[v]; return; } int mid=(ll+rr)>>1; if(p<=mid) Insert(v,p,rt<<1); else Insert(v,p,rt<<1|1); PushUp(rt);}int query(int l,int r,int rt){ int ll=node[rt].l,rr=node[rt].r; if(ll==l&&rr==r){ return node[rt].pre; } int mid=(ll+rr)>>1; if(r<=mid) return query(l,r,rt<<1); else if(l>mid) return query(l,r,rt<<1|1); else return max(query(l,mid,rt<<1),query(mid+1,r,rt<<1|1));}int main(){ while(~s(n)){ s(m); CLEAR(pre,0); CLEAR(sum,0); build(1,n,1); for(int i=1;i<=n;i++){ s(a); sum[i]+=sum[i-1]+a; Insert(a,i,1); pre[a]=i; } while(m--){ s(a);s(b); int tmp=(b-a+2)*(b-a+1)*1.0/2.0; // 如果是全排列那麼最後的和必然符合這個公式; if(sum[b]-sum[a-1]!=tmp){ printf("NO\n"); continue; }else{ if(query(a,b,1)<a) printf("YES\n"); // 查詢該區間最近一個重複的數字出現的位置,如果比a小,那就說明這個區間沒有出現過重複的數字; else printf("NO\n"); } } } return 0;}
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
HDU-5172-GTY's gay friends-線段樹單點更新