主席树入门
/*
主席樹入門從一個題目切入吧
HDU6230 Palindrome
最后轉化成求區間[l,r]里面有幾個數比x小
一開始就想無腦主席樹
之前只會了一個板子 很不靈活 只會查第k小
然后二分i是第幾小,套上主席樹 這時候會多一個二分logn
然后跑的比較慢在超時的邊緣試探
然后看到了劃分樹這個東西 還蠻好理解就寫了一發
依舊在超時的邊緣
其實查有幾個比x小的數不需要套那一層二分
只要理解好主席樹 就可以扔掉二分
下面簡單說一下我理解的這種比較神奇的數據結構也是根據題目來說 板子題 poj2104
我們開一顆線段樹 葉子節點就表示i出現的次數
準確的說 是前綴出現的次數 那就有n棵線段樹
查詢的時候利用前綴和的思想來做
下面優化空間
我們考慮從i到i+1發生了什么變化
只加進來一個數 之后從根節點到他的這條鏈發生了改變
因此我們只記錄這個東西
這樣就很好的解決了上面的問題
注意存的是每個數的個數 下標是數的大小 如果數字比較大要離散化然后這個是poj2104的代碼
*/
#include<cstdio>
#include<algorithm>
#define maxn 60010
#define mid (l+r)/2
using namespace std;
int n,m,num,a[maxn],A[maxn],s[maxn],lc[maxn],rc[maxn],r[maxn],cnt;
int init(){int x=0,f=1;char s=getchar();while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}return x*f;
}
int Build(int S,int L,int R){cnt++;s[cnt]=S;lc[cnt]=L;rc[cnt]=R;return cnt;
}
void Insert(int &now,int pre,int l,int r,int k){now=Build(s[pre]+1,lc[pre],rc[pre]);if(l==r)return;if(k<=mid)Insert(lc[now],lc[pre],l,mid,k);else Insert(rc[now],rc[pre],mid+1,r,k);
}
int Query(int L,int R,int l,int r,int k){if(l==r)return l;int sum=s[lc[R]]-s[lc[L]];if(sum>=k)return Query(lc[L],lc[R],l,mid,k);else return Query(rc[L],rc[R],mid+1,r,k-sum);
}
int main()
{n=init();m=init();for(int i=1;i<=n;i++){a[i]=init();A[i]=a[i];}sort(A+1,A+1+n);num=unique(A+1,A+1+n)-A-1;for(int i=1;i<=n;i++){int pos=lower_bound(A+1,A+1+num,a[i])-A;Insert(r[i],r[i-1],1,num,pos);}for(int i=1;i<=m;i++){int L=init(),R=init();int pos=(R-L)/2+1;pos=Query(r[L-1],r[R],1,num,pos);printf("%d\n",A[pos]);}return 0;
}
/*然后我們回到hdu6230 考慮快速查詢區間比x小的數的個數先看下施展套一個二分的無腦主席樹 2800ms+ (考慮到數字和下標一樣 就沒有離散化)
*/#include<iostream>
#include<cstdio>
#include<cstring>
#define mid (l+r)/2
#define maxn 500010
#define ll long long
using namespace std;
int T,n,len[maxn],a[maxn],s[maxn*30],lc[maxn*30],rc[maxn*30],r[maxn*30],cnt;
ll ans;
char c[maxn];
void Clear(){memset(len,0,sizeof(len));memset(s,0,sizeof(s));memset(c,0,sizeof(c));ans=0;cnt=0;
}
void Ready(){c[0]='#';c[n+1]='#';
}
void Mar(){int mx=-1,id=-1;for(int i=1;i<=n;i++){if(i<=id+mx)len[i]=min(len[2*id-i],id+mx-i);while(i-len[i]-1>=1&&i+len[i]+1<=n&&c[i-len[i]-1]==c[i+len[i]+1])len[i]++;if(i+len[i]>id+mx)id=i,mx=len[i];}
}
int Build(int S,int L,int R){cnt++;s[cnt]=S;lc[cnt]=L;rc[cnt]=R;return cnt;
}
void Insert(int &now,int pre,int l,int r,int k){now=Build(s[pre]+1,lc[pre],rc[pre]); if(l==r)return;if(k<=mid)Insert(lc[now],lc[pre],l,mid,k);else Insert(rc[now],rc[pre],mid+1,r,k);
}
int Query(int L,int R,int l,int r,int k){if(l==r)return l;int sum=s[lc[R]]-s[lc[L]];if(sum>=k)return Query(lc[L],lc[R],l,mid,k);else return Query(rc[L],rc[R],mid+1,r,k-sum);
}
void Solve(){for(int i=1;i<=n;i++)a[i]=i-len[i];for(int i=1;i<=n;i++){Insert(r[i],r[i-1],1,n,a[i]);}for(int i=1;i<=n;i++){int L=i+1,R=i+len[i];int Li=1,Ri=R-L+1;while(Li<=Ri){int Mid=(Li+Ri)/2;int pos=Query(r[L-1],r[R],1,n,Mid);if(pos<=i)Li=Mid+1;else Ri=Mid-1;}ans+=Li-1;}
}
int main(){scanf("%d",&T);while(T--){Clear();scanf("%s",c+1);n=strlen(c+1);Ready();Mar();Solve();printf("%lld\n",ans);}return 0;
}/*這個是套的劃分樹 時間差不多*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mid (l+r)/2
#define maxn 500010
#define ll long long
using namespace std;
int T,n,len[maxn],a[maxn],val[20][maxn],num[20][maxn];
ll ans;
char ss[maxn],c[maxn];
void Clear(){memset(len,0,sizeof(len));memset(a,0,sizeof(a));ans=0;memset(num[0],0,sizeof(num[0]));
}
void Ready(){c[0]='#';c[n+1]='#';
}
void Mar(){int mx=-1,id=-1;for(int i=1;i<=n;i++){if(i<=id+mx)len[i]=min(len[2*id-i],id+mx-i);while(i-len[i]-1>=1&&i+len[i]+1<=n&&c[i-len[i]-1]==c[i+len[i]+1])len[i]++;if(i+len[i]>id+mx)id=i,mx=len[i];}
}
void Build(int l,int r,int c){if(l==r)return;int isame=mid-l+1;//isame保存有多少和sorted[mid]一樣大的數進入左孩子for(int i=l;i<=r;i++)if(val[c][i]<a[mid])isame--;int ln=l,rn=mid+1;//本結點兩個孩子結點的開頭,ln左for(int i=l;i<=r;i++){if(i==l)num[c][i]=0;else num[c][i]=num[c][i-1];if(val[c][i]<a[mid]||(val[c][i]==a[mid]&&isame>0)){val[c+1][ln++]=val[c][i];num[c][i]++;if(val[c][i]==a[mid])isame--;}else val[c+1][rn++]=val[c][i];}Build(l,mid,c+1);Build(mid+1,r,c+1);
}
int Query(int c,int sl,int sr,int l,int r,int k){if(sl==sr)return val[c][sl];int ly;if(l==sl)ly=0;else ly=num[c][l-1];//ly 表示l 前面有多少元素進入左孩子int tolef=num[c][r]-ly; //這一層l到r之間進入左子樹的有tolef個if(tolef>=k){return Query(c+1,sl,(sl+sr)/2,sl+ly,sl+num[c][r]-1,k);}else{// l-sl 表示l前面有多少數,再減ly 表示這些數中去右子樹的有多少個int lr = (sl+sr)/2 + 1 + (l-sl-ly); //l-r 去右邊的開頭位置// r-l+1 表示l到r有多少數,減去去左邊的,剩下是去右邊的,去右邊1個,下標就是lr,所以減1return Query(c+1,(sl+sr)/2+1,sr,lr,lr+r-l+1-tolef-1,k-tolef);}
}
void Solve(){for(int i=1;i<=n;i++){a[i]=i-len[i];val[0][i]=a[i];}sort(a+1,a+1+n);Build(1,n,0);for(int i=1;i<=n;i++){int L=i+1,R=i+len[i];int Li=1,Ri=R-L+1;while(Li<=Ri){int Mid=(Li+Ri)/2;if(Query(0,1,n,L,R,Mid)<=i)Li=Mid+1;else Ri=Mid-1;}ans+=Li-1;}
}
int main(){scanf("%d",&T);while(T--){Clear();scanf("%s",c+1);n=strlen(c+1);Ready();Mar();Solve();printf("%lld\n",ans);}return 0;
}
/*很慢啊這樣子施展 隨便加幾組數據就要GG了
因為是按下標存的 找<=x的數的個數 實際上就是1-x的區間和
我們退化回線段樹的思想 就簡單的區間查詢就好了 去掉了二分
1200+ms */#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mid (l+r)/2
#define maxn 500010
#define ll long long
using namespace std;
int T,n,len[maxn],a[maxn],s[maxn*30],lc[maxn*30],rc[maxn*30],r[maxn*30],cnt;
ll ans;
char c[maxn];
void Clear(){memset(len,0,sizeof(len));memset(c,0,sizeof(c));ans=0;cnt=0;
}
void Ready(){c[0]='#';c[n+1]='#';
}
void Mar(){int mx=-1,id=-1;for(int i=1;i<=n;i++){if(i<=id+mx)len[i]=min(len[2*id-i],id+mx-i);while(i-len[i]-1>=1&&i+len[i]+1<=n&&c[i-len[i]-1]==c[i+len[i]+1])len[i]++;if(i+len[i]>id+mx)id=i,mx=len[i];}
}
int Build(int S,int L,int R){cnt++;s[cnt]=S;lc[cnt]=L;rc[cnt]=R;return cnt;
}
void Insert(int &now,int pre,int l,int r,int k){now=Build(s[pre]+1,lc[pre],rc[pre]);if(l==r)return;if(k<=mid)Insert(lc[now],lc[pre],l,mid,k);else Insert(rc[now],rc[pre],mid+1,r,k);
}
int Query(int L,int R,int x,int y,int l,int r){if(x<=l&&y>=r)return s[R]-s[L];int res=0;if(x<=mid)res+=Query(lc[L],lc[R],x,y,l,mid);if(y>mid)res+=Query(rc[L],rc[R],x,y,mid+1,r);return res;
}
void Solve(){for(int i=1;i<=n;i++)a[i]=i-len[i];for(int i=1;i<=n;i++)Insert(r[i],r[i-1],1,n,a[i]);for(int i=1;i<=n;i++){int L=i+1,R=i+len[i];if(L>R)continue;ans+=Query(r[L-1],r[R],1,i,1,n);}
}
int main(){scanf("%d",&T);while(T--){Clear();scanf("%s",c+1);n=strlen(c+1);Ready();Mar();Solve();printf("%lld\n",ans);}return 0;
}
?
轉載于:https://www.cnblogs.com/yanlifneg/p/9544175.html
總結
- 上一篇: 机器学习基础一(TP,TN,FP,FN等
- 下一篇: UPC2018组队训练赛第六场