日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构小总结(成都磨子桥技工学校数据结构前12题)

發布時間:2023/12/9 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构小总结(成都磨子桥技工学校数据结构前12题) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


[pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=34352147
暑假的作業,頹頹的我總算是寫完了

線段樹

線段樹是一個高級玩意,不僅可以求區間和,區間最大等等的簡單問題,靈活運用還有好多變種。自從學了主席樹,知道了null自環這種東西后,用在線段樹上也是得心應手

c3

給一個長為N的數列,有M次操作,每次操作是以下兩種之一:
(1)修改數列中的一個數
(2)求數列中某連續一段的和

赤裸裸的線段樹

c4

給一個長為N的數列,有M次操作,每次操作時以下三種之一:
(1)修改數列中的一個數
(2)求數列中某連續一段所有數的兩兩乘積的和 mod 1000000007
(3)求數列中某連續一段所有相鄰兩數乘積的和 mod 1000000007

數據劇毒無比,有負數,取模就出問題了。對于區間維護答案,主要就是如何合并區間。操作3好合并,只要記錄每個區間的頭、尾的數,把左右兒子區間的和加起來,再加上中間兩個數的乘積。關鍵是操作2,要是沒有見識過這個腦筋急轉彎,我可能一輩子都不會:

給出N個數, 每次可以合并兩個數, 合并的代價是兩個數的乘積, 合并得到的數是兩個數的和。
問最后把所有數合并成一個數的最小代價。 求這個最小代價對10^9+7取模的結果。
N <= 5000000。

題解是:

顯然無論怎么合并答案都是一樣的, 任意兩個數的乘積恰好會對答案貢獻一次。
直接搞就好了

于是這道題的操作2就迎刃而解了
由于這道題坑很多,我就不放我wa掉的代碼了
大神的AC代碼

#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int maxn=100005; const int mod=1000000007; struct Node{int s1,s2,s3; }node[maxn<<2]; int a[maxn]; int n,m; int slow_mult(int a,int p){if(a<0)a=(a+(mod<<1))%mod;if(p<0)p =(p+(mod<<1))%mod;if(a<p)swap(a,p);if(p==0)return 0;if(p==1)return a%mod;int tmp=slow_mult(a,p>>1);if(p&1)return ((tmp<<1)%mod+a%mod)%mod;else return (tmp<<1)%mod; } inline void update(int root,int l,int r){node[root].s1=(node[root<<1].s1+node[root<<1|1].s1)%mod;node[root].s2=(node[root<<1].s2+node[root<<1|1].s2)%mod;int m=(l+r)>>1;node[root].s3=((node[root<<1].s3+node[root<<1|1].s3)%mod+slow_mult(a[m],a[m+1]))%mod; } void build(int root,int l,int r){if(l==r){node[root].s1=a[l];node[root].s2=slow_mult(a[l],a[r]);node[root].s3=0;return ;}int m=(l+r)>>1;build(root<<1,l,m),build(root<<1|1,m+1,r);update(root,l,r); } void modify(int root,int l,int r,int x,int val){if(l==r){node[root].s1=val;node[root].s2=slow_mult(val , val);node[root].s3=0;a[x]=val;return;}int m=(l+r)>>1;if(x<=m)modify(root<<1,l,m,x,val);else modify(root<<1|1,m+1,r,x,val);update(root,l,r); } int query1(int root,int l,int r,int x,int y){if(x<=l&&r<=y)return node[root].s1;int m=(l+r)>>1,ret=0;if(x<=m&&l<=y)ret+=query1(root<<1,l,m,x,y);if(y>=m+1&&r>=x)ret+=query1(root<<1|1,m+1,r,x,y);return ret % mod; } int query2(int root,int l,int r,int x,int y){if(x<=l&&r<=y)return node[root].s2;int m=(l+r)>>1,ret=0;if(x<=m&&l<=y)ret+=query2(root<<1,l,m,x,y);if(y>=m+1&&r>=x)ret+=query2(root<<1|1,m+1,r,x,y);return ret % mod; } int query3(int root,int l,int r,int x,int y){if(x<=l&&r<=y)return node[root].s3;int m=(l+r)>>1,ret=0,flag=1;if(x<=m&&l<=y)ret+=query3(root<<1,l,m,x,y),flag*=-1;if(y>=m+1&&r>=x)ret+=query3(root<<1|1,m+1,r,x,y),flag*=-1;if(flag^1)return ret%mod;else return(ret%mod+slow_mult(a[m],a[m+1]))%mod; } void read(int &res){int flag=1;static char ch;while((ch=getchar())<'0'||ch>'9')if(ch=='-')flag=-1;res=ch-48;while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-48;res*=flag; } void reads(char &res){static char ch;while((ch=getchar())!='Q'&&ch!='M'&&ch!='A');res=ch; } int main(){read(n),read(m);for(int i=1;i<=n;i++)read(a[i]),a[i]%=mod;build(1,1,n);for(int i=1;i<=m;i++){char cmd;int x,y;reads(cmd);read(x),read(y);if(cmd=='M')modify(1,1,n,x,y%mod);else if(cmd=='Q'){int t1=query1(1,1,n,x,y);int t2=query2(1,1,n,x,y);t1=slow_mult(t1,t1);int tmp=t1-t2;if(tmp<0||tmp&1)tmp+=mod;if(tmp&1)tmp+=mod;printf("%d\n",(tmp>>1)%mod);}else printf("%d\n",query3(1,1,n,x,y));}return 0; }

c5

給一個長為N的數列,有M次操作,每次操作是以下兩種之一:
(1)將某連續一段同時改成一個數
(2)求數列中某連續一段的和

也是很基本的線段樹

c6

給一個長為N的數列,有M次操作,每次操作是以下兩種之一:
(1)修改數列中的一個數
(2)求數列中有多少個數比它前面的數都大

其實是裸的樓房重建,線段樹代碼見這里
然后聽說這道題用分塊也能過,為了練習練習,這次就用分塊了

#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<vector> using namespace std;const int N=100000+5; const int B=400; const int oo=0x7fffffff;int n,m,hh[N]; vector<int> a[B]; int blk,pos[N];void getsort(int x){int maxx=-oo;a[x].clear();for(int i=(x-1)*blk+1;i<=min(n,x*blk);i++){if(hh[i]>maxx){a[x].push_back(i);maxx=hh[i];} } } void init(){for(int i=1;i<=pos[n];i++){getsort(i);} } int erfen(int maxx,int x){int le=1,ri=a[x].size();while(le<ri){int mid=(le+ri)>>1;if(hh[a[x][mid-1]]<=maxx) le=mid+1;else ri=mid; }if(hh[a[x][le-1]]<=maxx){return 0;} return a[x].size()-le+1; } int query(){int ans=0,maxx=-oo;for(int i=1;i<=pos[n];i++){ans+=erfen(maxx,i);maxx=max(maxx,hh[a[i][a[i].size()-1]]);}return ans; } int main(){scanf("%d%d",&n,&m);blk=(int)sqrt((double)n);for(int i=1;i<=n;i++){scanf("%d",&hh[i]);pos[i]=(i-1)/blk+1;} init();while(m--){char opt[2];scanf("%s",opt);if(opt[0]=='M'){int x,y;scanf("%d%d",&x,&y);hh[x]=y;getsort(pos[x]);}else{printf("%d\n",query());}}return 0; }

c7

給一個長為N的數列,有M次操作,每次操作是以下兩種之一:
(1)修改數列中的一個數
(2)求數列中某個值出現了多少次

在做c8之前是沒有想到用值域線段樹的,以為會爆空間(int級別的),就偷了個懶,用map
如何處理值域線段樹的空間問題,見c8

#include<cstdio> #include<cstring> #include<map> #include<algorithm> using namespace std;const int N=100000+5;map<int,int> mp; int a[N];int main(){int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%d",&a[i]);mp[a[i]]++;}while(m--){char opt[2];scanf("%s",opt);if(opt[0]=='M'){int x,y;scanf("%d%d",&x,&y);mp[a[x]]--;mp[y]++;a[x]=y;}else{int x;scanf("%d",&x);printf("%d\n",mp[x]);}}return 0; }

c8

給一個長為N的數列,有M次操作,每次操作是以下三種之一:
(1)插入一個數,若已存在則忽略
(2)刪除一個數,若不存在則忽略
(3)求數列中任意兩數之差絕對值的最小值

先不考慮空間問題,我一來就想到值域線段樹。計算存在的數與最近的前后兩數的差值的最小值。每個區間儲存存在的最小數和最大數。合并時,左區間的ans,右區間的ans,左區間最大數和右區間的最小數的差,取min。
那么怎么處理空間大小問題呢?數據是2^31,但N,M的范圍是10^5,也就是說最少都有2^31-10^5的數根本和此題無關,是浪費空間。其維護的值都是一樣的,那么為什么不指向同一個空間呢?于是就把主席樹里的null搬過來,不存在的值就由null代替
然后我猜數據里有負數,結果在劃分區間是沒注意向上還是向下取整的問題,結果T掉了,全部都加上正無窮就好了。之后又沒開longlong,int加爆了。。。orz

#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std;const ll oo=2147483646; const ll N=100000+5;ll n,m; struct Node{Node *ls,*rs;ll ans,maxn,minn,cnt; }*root,*null,pool[N*80],*tail=pool;Node *newnode(){Node *rt=++tail;rt->ls=rt->rs=null;rt->ans=oo;rt->maxn=rt->minn=rt->cnt=0;return rt; } void update(Node *nd){nd->cnt=nd->ls->cnt+nd->rs->cnt;nd->ans=oo;if(nd->ls->cnt>1) nd->ans=min(nd->ans,nd->ls->ans);if(nd->rs->cnt>1) nd->ans=min(nd->ans,nd->rs->ans);if(nd->ls->cnt&&nd->rs->cnt) nd->ans=min(nd->ans,nd->rs->minn - nd->ls->maxn);if(nd->rs->cnt) nd->maxn=nd->rs->maxn;else nd->maxn=nd->ls->maxn;if(nd->ls->cnt) nd->minn=nd->ls->minn;else nd->minn=nd->rs->minn; } void insert(Node *&nd,ll le,ll ri,ll pos){if(nd==null) nd=newnode();if(le==ri){nd->cnt=1;nd->ans=oo;nd->maxn=nd->minn=pos;return;}ll mid=(le+ri)/2;if(pos<=mid) insert(nd->ls,le,mid,pos);else insert(nd->rs,mid+1,ri,pos);update(nd); } void del(Node *&nd,ll le,ll ri,ll pos){if(nd==null) return ;if(le==ri){nd->cnt=0;nd->ans=nd->maxn=nd->minn=0;return ;}ll mid=(le+ri)/2;if(pos<=mid) del(nd->ls,le,mid,pos);else del(nd->rs,mid+1,ri,pos);update(nd); } int main(){null=++tail;null->ls=null->rs=null;null->ans=null->minn=null->maxn=null->cnt=0;root=null;scanf("%lld%lld",&n,&m);ll a;for(ll i=1;i<=n;i++){scanf("%lld",&a);insert(root,0,oo+oo,a+oo);}while(m--){char opt[2];ll x;scanf("%s",opt);if(opt[0]=='I'){scanf("%lld",&x);insert(root,0,oo+oo,x+oo);}else if(opt[0]=='D'){scanf("%lld",&x);del(root,0,oo+oo,x+oo);}else {if(root->cnt>1) printf("%lld\n",root->ans);else printf("-1\n");}} }

c10

給一個長為N的數列,有M次操作,每次操作是以下兩種之一:
(1)修改數列中的一個數
(2)求數列中第K小的值

第k大問題,想來都是線段樹。由于每次要修改數據,離散化不太可能,那么就用上文提到的null來節省空間(哎呀我真是太機智了,自己yy出來的誒,也算是進步吧)

#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std;const ll N=100000+5; const ll oo=2147483646;struct Node {Node *ls,*rs;ll cnt; }*root,*null,pool[N*80],*tail=pool; ll n,m,a[N];Node *newnode(){Node *rt=++tail;rt->ls=rt->rs=null;rt->cnt=0;return rt; } void modify(Node *&nd,ll le,ll ri,ll pos,ll val){if(nd==null) nd=newnode();if(le==ri){nd->cnt+=val;return ;}ll mid=(le+ri)>>1;if(pos<=mid) modify(nd->ls,le,mid,pos,val);else modify(nd->rs,mid+1,ri,pos,val);nd->cnt=nd->ls->cnt+nd->rs->cnt; } ll query(Node *nd,ll le,ll ri,ll pos){if(le==ri)return le;ll mid=(le+ri)>>1;if(pos<=nd->ls->cnt) return query(nd->ls,le,mid,pos);else return query(nd->rs,mid+1,ri,pos-nd->ls->cnt); } int main(){null=++tail;null->ls=null->rs=null;null->cnt=0;root=null;scanf("%lld%lld",&n,&m);for(ll i=1;i<=n;i++){scanf("%lld",&a[i]);modify(root,0,oo+oo,a[i]+oo,1);}while(m--){char opt[2];scanf("%s",opt);if(opt[0]=='Q'){ll x;scanf("%lld",&x);printf("%lld\n",query(root,0,oo+oo,x)-oo);}else {ll x,y;scanf("%lld%lld",&x,&y);modify(root,0,oo+oo,a[x]+oo,-1);modify(root,0,oo+oo,y+oo,1);a[x]=y;}}return 0; }

總結:
線段樹可膩害可膩害了,其實很多的區間問題都可以解決,唯一的關鍵點就是如何快速的區間合并。俗話說“以不變應萬變”,只要解決區間合并的方法就可以了。

主席樹

c2

給一個空數列,有M次操作,每次操作是以下三種之一:
(1)在數列后加一個數
(2)求數列中某位置的值
(3)撤銷掉最后進行的若干次操作(1和3)

感覺像是數組,又感覺像是主席樹。但是數組又不易用指針寫,于是一氣之下殺雞用牛刀,用線段樹來解決數列問題。。。1A

#include<cstdio> #include<cstring> #include<algorithm> using namespace std;const int N=100000+5;struct Node{Node *ls,*rs;int val; }*root[N],*null,pool[N*50],*tail=pool; int len[N],m;Node *newnode(){Node *rt=++tail;rt->ls=rt->rs=null;rt->val=0;return rt; } void insert(Node *np,Node *&nd,int le,int ri,int pos,int val){nd=newnode();nd->ls=np->ls,nd->rs=np->rs;nd->val=np->val;if(le==ri){nd->val=val;return ;}int mid=(le+ri)>>1;if(pos<=mid) insert(np->ls,nd->ls,le,mid,pos,val);else insert(nd->rs,nd->rs,mid+1,ri,pos,val); } int query(Node *nd,int le,int ri,int pos){if(le==ri) return nd->val;int mid=(le+ri)>>1;if(pos<=mid) return query(nd->ls,le,mid,pos);else return query(nd->rs,mid+1,ri,pos); } int main(){null=++tail;null->ls=null->rs=null;null->val=0;root[0]=null;scanf("%d",&m);int cnt=0;while(m--){char opt[2];int x;scanf("%s%d",opt,&x);if(opt[0]=='A'){cnt++;len[cnt]=len[cnt-1]+1;insert(root[cnt-1],root[cnt],1,N,len[cnt],x);}else if(opt[0]=='Q'){printf("%d\n",query(root[cnt],1,N,x));}else{cnt++;root[cnt]=root[cnt-x-1];len[cnt]=len[cnt-x-1]; }}return 0; }

c11

給一個長為N的數列,有M次操作,每次操作是以下兩種之一:
(1)修改數列中的一個數
(2)求某次操作后連續一段的和

裸主席樹,就當練手速吧

#include<cstdio> #include<cstring> #include<algorithm> using namespace std;const int N=100000+5;struct Node{Node *ls,*rs;int sum; }*root[N],*null,pool[N*80],*tail=pool; int n,m,a[N];Node *newnode(){Node *rt=++tail;rt->ls=rt->rs=null;rt->sum=0;return rt; } void build(Node *&nd,int le,int ri){nd=newnode();if(le==ri){nd->sum=a[le];return ;}int mid=(le+ri)>>1;build(nd->ls,le,mid);build(nd->rs,mid+1,ri);nd->sum=nd->ls->sum+nd->rs->sum; } void insert(Node *ne,Node *&nd,int le,int ri,int pos,int val){nd=newnode();nd->ls=ne->ls,nd->rs=ne->rs;if(le==ri){nd->sum=val;return ;}int mid=(le+ri)>>1;if(pos<=mid) insert(ne->ls,nd->ls,le,mid,pos,val);else insert(ne->rs,nd->rs,mid+1,ri,pos,val);nd->sum=nd->ls->sum+nd->rs->sum; } int query(Node *nd,int le,int ri,int L,int R){if(L<=le&&ri<=R) return nd->sum;int mid=(le+ri)>>1,rt=0;if(L<=mid) rt+=query(nd->ls,le,mid,L,R);if(mid<R) rt+=query(nd->rs,mid+1,ri,L,R);return rt; } int main(){null=++tail;null->ls=null->rs=null;null->sum=0;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) scanf("%d",&a[i]);build(root[0],1,n);for(int i=1;i<=m;i++){char opt[2];scanf("%s",opt);if(opt[0]=='M'){int x,y;scanf("%d%d",&x,&y);insert(root[i-1],root[i],1,n,x,y);}else{int x,y,z;scanf("%d%d%d",&x,&y,&z);printf("%d\n",query(root[z],1,n,x,y));root[i]=root[i-1];}}return 0; }

c12

給一個長為N的數列,有M次操作,操作僅有一種:
求數列中某連續一段中第K小的值

還是裸的主席樹,沒錯我是來掛代碼的

#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std;const ll N=100000+5; const ll oo=2147483646;struct Node {Node *ls,*rs;ll cnt; }*root[N],*null,pool[N*100],*tail=pool; ll n,m,a[N];Node *newnode(){Node *rt=++tail;rt->ls=rt->rs=null;rt->cnt=0;return rt; } void insert(Node *ne,Node *&nd,ll le,ll ri,ll pos){nd=newnode();nd->ls=ne->ls,nd->rs=ne->rs;nd->cnt=ne->cnt+1;if(le==ri) return;ll mid=(le+ri)>>1;if(pos<=mid) insert(ne->ls,nd->ls,le,mid,pos);else insert(ne->rs,nd->rs,mid+1,ri,pos); } ll query(Node *ne,Node *nd,ll le,ll ri,ll k){if(le==ri) return le;ll mid=(le+ri)>>1;ll lsc=nd->ls->cnt - ne->ls->cnt;if(k<=lsc) return query(ne->ls,nd->ls,le,mid,k);else return query(ne->rs,nd->rs,mid+1,ri,k-lsc); } int main(){null=++tail;null->ls=null->rs=null;null->cnt=0;root[0]=null;scanf("%lld%lld",&n,&m);for(ll i=1;i<=n;i++){scanf("%lld",&a[i]);insert(root[i-1],root[i],0,oo+oo,a[i]+oo);}while(m--){ll x,y,z;scanf("%lld%lld%lld",&x,&y,&z);printf("%lld\n",query(root[x-1],root[y],0,oo+oo,z)-oo);}return 0; }

數組

c0,c1
太簡單啦,寒假做的

平衡樹

c9

給一個長為N的數列,有M次操作,每次操作是以下兩種之一:
(1)刪除某個位置的數
(2)求數列某位置的值

一來就想到treap,但是感覺大材小用了,就偷了個懶

#include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std;vector<int> vec; int n,m;int main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){int a;scanf("%d",&a);vec.push_back(a);}while(m--){char opt[2];int x;scanf("%s%d",opt,&x);x--;if(opt[0]=='D')vec.erase(vec.begin()+x);else printf("%d\n",vec[x]);}return 0; }

好啦,就是這樣

轉載于:https://www.cnblogs.com/LinnBlanc/p/7763147.html

總結

以上是生活随笔為你收集整理的数据结构小总结(成都磨子桥技工学校数据结构前12题)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。