主席树复习
T1?[CQOI2015]任務查詢系統
n個任務,每個有運行的時間段和優先級,詢問某一時刻,優先級最小的個任務的優先級之和
?
初做: ?2017.2.4 ??http://www.cnblogs.com/TheRoadToTheGold/p/6366165.html
好像是做了一晚上來
現在:2017.3.27 ? 14:17——15:56
用了接近2個小時做了一道以前做過的題,還是弱啊~~~~(>_<)~~~~
?
difference:
主席樹維護的東西不同,以前直接存儲優先級之和,現在存儲的是任務個數,其實一個樣。。
再就是預處理方式不同
離散化優先級,以時間為下標,以優先級為區間,建立主席樹
?
對主席樹理解不透徹,開始離散化優先級,又以優先級為下標,建主席樹
離散化了某個值后,它就成了線段樹中節點的可控區間范圍
?
個人理解:
主席樹中節點有3類屬性,下標(自己的、孩子的)、可控區間范圍、要維護的值
下標一般存在root[]、lc[]、rc[]中,要維護的值用各種數組存儲
在線段樹中,可控區間范圍是作為節點信息儲存的,但主席樹一般不存儲
在添加或查詢時,加上2個參數,表示當前節點的可控區間范圍,在遞歸過程中,范圍與當前節點保持一致
?
代碼模板化,所以導致在寫的時候
添加節點的pre上來就寫root[i-1],這個題是以自身為參照
查詢的時候上來就root[i-1],這里不需要參照
?
一大問題:
這里要保持時間的連續性,若一個任務在[l,r]時間執行
代碼中是l位置+1,r位置-1,
所以在添加操作之前,先令root[i]=root[i-1]
目的有二:
1、若某一個時刻在輸入中沒有出現,這樣可以保持時間的連續性
2、添加節點是以自身為參照
?
開始40分RE,原因:一個任務往主席樹中加入2個點,所以數組應*40
#include<cstdio> #include<algorithm> #define N 200001 using namespace std; struct node2 {int time,p,w; }g[N*2]; int hash[N]; int n,m,tot,id; int root[N*20],lc[N*20],rc[N*20],cnt[N*20]; long long ans=1; bool cmp(node2 k,node2 q) {return k.time<q.time; } void discrete() {sort(hash+1,hash+m+1);tot=unique(hash+1,hash+m+1)-hash-1;for(int i=1;i<=2*m;i++) g[i].p=lower_bound(hash+1,hash+tot+1,g[i].p)-hash; } void insert(int pre,int &now,int l,int r,int pos,int val) {now=++id;cnt[now]=cnt[pre]+val;if(l==r) return;int mid=l+r>>1;if(pos<=mid){rc[now]=rc[pre];insert(lc[pre],lc[now],l,mid,pos,val);}else{lc[now]=lc[pre];insert(rc[pre],rc[now],mid+1,r,pos,val);} } void query(int now,int l,int r,int k) {if(l==r) {ans+=1ll*min(cnt[now],k)*hash[l];return;}int mid=l+r>>1,tmp=cnt[lc[now]];query(lc[now],l,mid,k);if(k>tmp) query(rc[now],mid+1,r,k-tmp); } int main() {/*freopen("cqoi15_query.in","r",stdin);freopen("cqoi15_query.out","w",stdout);*/scanf("%d%d",&m,&n);int x,k,a,b,c;for(int i=1;i<=m;i++){scanf("%d%d%d",&a,&b,&c);g[i*2-1].time=a; g[i*2-1].p=c; g[i*2-1].w=1;g[i*2].time=b+1; g[i*2].p=c; g[i*2].w=-1;hash[i]=c;}sort(g+1,g+2*m+1,cmp);discrete();int last=0;for(int i=1;i<=2*m;i++) {for(int j=last+1;j<=g[i].time;j++){root[j]=root[j-1];}insert(root[g[i].time],root[g[i].time],1,tot,g[i].p,g[i].w);last=g[i].time;}for(int i=1;i<=n;i++){scanf("%d%d%d%d",&x,&a,&b,&c);k=(ans*a+b)%c+1;ans=0;query(root[x],1,tot,k);printf("%lld\n",ans);} } View Code?
?
T2 ?APIO2012 dispatching
每個點有價值、花費,所有的點構成一棵樹,給出資金限制
num[i]=在以i為根的子樹中,在資金限制內最多能選的點的個數
最大化 s=i的價值*num[i]
?
初做:2017.2.5 http://www.cnblogs.com/TheRoadToTheGold/p/6368387.html
現在:2017.3.27
16:31做到19:32 中間吃了個飯聽了個聽力
?
主席樹+dfs序
離散化費用,以費用為區間,以點的dfs序為下標建立主席樹
?
剛開始想的是枚舉每個點,然后二分最多能選的點
其實不用二分
用主席樹維護到這個點的費用和、點數
然后求在滿足費用和<=資金限制的情況下,最多選多少點
?
很巧妙的是dfs序的應用,但在這兒不是我關注的重點
由于點又有了dfs序,所以一開始就把點的原編號與dfs序搞混了
?
最大的錯誤:
查詢時,不思考就int mid=l+r>>1,tmp。。。。。。
這里tmp要用long long,卡了1個多小時
模板背熟了是好,注意應用?
#include<cstdio> #include<algorithm> #define N 100001 using namespace std; int n,m,se,cnt; int front[N],nextt[N],to[N],tot_ninja; int money[N],hashh[N],tot,lead[N]; int in[N],out[N],id[N],s;//id[i]=j :編號為j的是i號忍者 int root[N],lc[N*20],rc[N*20],num[N*20]; long long sum[N*20]; void add(int u,int v) {nextt[++tot_ninja]=front[u]; front[u]=tot_ninja; to[tot_ninja]=v; } void dfs(int now) {for(int i=front[now];i;i=nextt[i]){id[++s]=to[i];in[to[i]]=s;dfs(to[i]);}out[now]=s; } void insert(int pre,int &now,int l,int r,int pos) {now=++se;num[now]=num[pre]+1;sum[now]=sum[pre]+hashh[pos];if(l==r) return;int mid=l+r>>1;if(pos<=mid) {rc[now]=rc[pre];insert(lc[pre],lc[now],l,mid,pos); }else{lc[now]=lc[pre];insert(rc[pre],rc[now],mid+1,r,pos);} } void discrete() {sort(hashh+1,hashh+n+1);tot=unique(hashh+1,hashh+n+1)-hashh-1;for(int i=1;i<=n;i++) money[i]=lower_bound(hashh+1,hashh+tot+1,money[i])-hashh; } void query(int pre,int now,int l,int r,int limit) {if(l==r){cnt+=min(limit/hashh[l],num[now]-num[pre]);return;} int mid=l+r>>1;long long tmp=sum[lc[now]]-sum[lc[pre]];if(limit<=tmp) query(lc[pre],lc[now],l,mid,limit);else{cnt+=num[lc[now]]-num[lc[pre]];query(rc[pre],rc[now],mid+1,r,limit-tmp);} } int main() {freopen("dispatching.in","r",stdin);freopen("dispatching.out","w",stdout);scanf("%d%d",&n,&m);int x;for(int i=1;i<=n;i++){scanf("%d%d%d",&x,&money[i],&lead[i]);hashh[i]=money[i];add(x,i);}discrete();dfs(0);for(int i=1;i<=n;i++) insert(root[i-1],root[i],1,tot,money[id[i]]);long long ans=0;for(int i=1;i<=n;i++){cnt=0;query(root[in[i]-1],root[out[i]],1,tot,m);ans=max(ans,(long long)lead[i]*cnt);}printf("%lld",ans); } View Code?
轉載于:https://www.cnblogs.com/TheRoadToTheGold/p/6627353.html
總結
- 上一篇: 万王之王射手转什么(万方有什么区别)
- 下一篇: 水平垂直居中的方法