JZOJ 5909. 【NOIP2018模拟10.16】跑商(paoshang)
Description
題目背景:
尊者神高達很窮,所以他需要跑商來賺錢
題目描述:
基三的地圖可以看做 n 個城市,m 條邊的無向圖,尊者神高達會從任意一個點出發并在起點購買貨物,在旅途中任意一點賣出并最終到達終點,尊者神高達的時間很寶貴,所以他不會重復經過同一個城市,但是為了掙錢,他可能會去繞路。當然,由于工作室泛濫,所以一個城市的貨物價格可能會發生改變。但是尊者神高達智商不足,他可能在一個很蠢的節點把貨物賣掉,所以尊者神高達想知道每一次跑商最多能賠多少錢。
Input
第一行 n,m;
接下來 1 行 n 個數,代表每個城市貨物的價格;
接下來 m 行 u,v 代表一條邊
接下來 1 行 Q
接下來 Q 行
C x w 代表城市 x 的貨物價格變為 w
Q u v 代表一次從 u 到 v 的跑商
Output
如題目描述
Sample Input
3 3
1 2 3
1 2
2 3
1 3
3
Q 2 3
C 1 5
Q 1 3
Sample Output
1
3
樣例解釋:
1,2,3 都聯通,起點購買價格為 2,在 1 點賣出賠得最多2-1=1
更新后每個點價值為 5,2,3
起點價格為 5,在 2 點賣出賠得最多,5-2=3
Data Constraint
40%的數據為一棵樹
另外 20%的數據沒有修改操作
所以數據滿足 n,m,q<=100000;保證圖聯通,數據合法
Solution
-
40%的數據就是裸的樹鏈剖分,查詢區間最小值、單點修改即可。
-
而變成無向圖后呢,就要用到圓方樹了!
-
圓方樹是處理仙人掌的利器,應用廣泛。
-
嗯,首先,它是一棵樹,由圓點和方點組成。
-
原圖中就存在的點被稱為圓點,我們將其縮點雙聯通分量后,新建一個方點來代表這個點雙,
-
并將點雙中的點與這個方點連邊(原來點雙里面的邊就不要了),縮點雙用 tarjan 算法。
-
建出來的圓方樹的圓點和方點是相隔的,即不會有圓點連圓點、方點連方點。
-
有了這顆圓方樹,我們就能解決很多問題了!
-
在本題中,圓點的權值就設為其本來的值(即 aia_iai?),而方點的值就是點雙中點權的最小值。
-
注意: 這里方點的值不包括其父親圓點的(這樣好算很多)。
-
于是查詢就能用樹鏈剖分求最小值了,注意若兩點lca是方點,還得算上其父親圓點的值。
-
而查詢的話,對于該圓點就單點修改,對于其屬于的方點就用一個set(可維護插入刪除,求最小值的數據結構)維護其包含的圓點值即可。
-
時間復雜度 O(nlog2n)O(n\ log^2n)O(n?log2n) 。
Code
#include<cstdio> #include<algorithm> #include<set> #include<cctype> using namespace std; const int N=1e5+5,inf=1e9; int n,m,tot=1,num,cnt,rt,qx,qy,stop,ans; int first[N],nex[N<<1],en[N<<1]; int first1[N<<1],nex1[N<<1],en1[N<<1],d[N<<1]; int a[N]; int dfn[N],low[N],st[N],id[N]; int fa[N<<1],dep[N<<1],size[N<<1],son[N<<1]; int top[N<<1],tree[N<<1],pre[N<<1],f[N<<3]; multiset<int>ss[N<<1]; multiset<int>::iterator it; inline int read() {int X=0,w=0; char ch=0;while(!isdigit(ch)) w|=ch=='-',ch=getchar();while(isdigit(ch)) X=(X<<1)+(X<<3)+(ch^48),ch=getchar();return w?-X:X; } inline void insert(int x,int y) {nex[++tot]=first[x];first[x]=tot;en[tot]=y; } inline void insert1(int x,int y) {nex1[++tot]=first1[x];first1[x]=tot;en1[tot]=y;d[y]++; } inline int min(int x,int y) {return x<y?x:y; } void tarjan(int x,int y) {dfn[x]=low[x]=++num;st[++stop]=x;for(int i=first[x];i;i=nex[i])if(!dfn[en[i]]){tarjan(en[i],i^1);low[x]=min(low[x],low[en[i]]);if(low[en[i]]>=dfn[x]){cnt++;do{id[st[stop]]=cnt;insert1(cnt,st[stop]);ss[cnt].insert(a[st[stop]]);}while(st[stop--]^en[i]);insert1(x,cnt);}}elseif(i^y) low[x]=min(low[x],dfn[en[i]]); } void dfs(int x) {dep[x]=dep[fa[x]]+1;size[x]=1;for(int i=first1[x];i;i=nex1[i])if(en1[i]^fa[x]){fa[en1[i]]=x;dfs(en1[i]);size[x]+=size[en1[i]];if(!son[x] || size[son[x]]<size[en1[i]]) son[x]=en1[i];} } void dfs1(int x,int y) {top[pre[tree[x]=++num]=x]=y;if(!son[x]) return;dfs1(son[x],y);for(int i=first1[x];i;i=nex1[i])if(en1[i]^fa[x] && en1[i]^son[x]) dfs1(en1[i],en1[i]); } void make(int v,int l,int r) {if(l==r){if(pre[l]>n){it=ss[pre[l]].begin();f[v]=*it;}else f[v]=a[pre[l]];return;}int mid=l+r>>1;make(v<<1,l,mid);make(v<<1|1,mid+1,r);f[v]=min(f[v<<1],f[v<<1|1]); } int find(int v,int l,int r) {if(qx<=l && r<=qy) return f[v];int mid=l+r>>1;int s=inf;if(qx<=mid) s=find(v<<1,l,mid);if(qy>mid) s=min(s,find(v<<1|1,mid+1,r));return s; } void change(int v,int l,int r) {if(l==r){f[v]=qy;return;}int mid=l+r>>1;if(qx<=mid) change(v<<1,l,mid); else change(v<<1|1,mid+1,r);f[v]=min(f[v<<1],f[v<<1|1]); } int main() {freopen("paoshang.in","r",stdin);freopen("paoshang.out","w",stdout);n=cnt=read(),m=read();for(int i=1;i<=n;i++) a[i]=read();for(int i=1;i<=m;i++){int x=read(),y=read();insert(x,y);insert(y,x);}tarjan(1,tot=0);for(int i=1;i<=cnt;i++)if(!d[i]){rt=i;break;}dfs(rt);num=0;dfs1(rt,rt);make(1,1,num);int q=read();while(q--){char ch=getchar();while(ch^'Q' && ch^'C') ch=getchar();if(ch=='Q'){int x=read(),y=read(),st=a[x];ans=inf;int f1=top[x],f2=top[y];while(f1^f2){if(dep[f1]<dep[f2]) swap(f1,f2),swap(x,y);qx=tree[f1],qy=tree[x];ans=min(ans,find(1,1,num));x=fa[f1],f1=top[x];}if(dep[x]>dep[y]) swap(x,y);qx=tree[x],qy=tree[y];ans=min(ans,find(1,1,num));if(x>n) ans=min(ans,a[fa[x]]);printf("%d\n",st-ans);}else{int x=read(),w=read();qx=tree[x],qy=w;change(1,1,num);int y=id[x];if(y){it=ss[y].find(a[x]);ss[y].erase(it);ss[y].insert(w);it=ss[y].begin();qx=tree[y],qy=*it;change(1,1,num);}a[x]=w;}}return 0; }總結
以上是生活随笔為你收集整理的JZOJ 5909. 【NOIP2018模拟10.16】跑商(paoshang)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JZOJ 5904. 【NOIP2018
- 下一篇: JZOJ 5907. 【NOIP2018