BZOJ3123: [Sdoi2013]森林
BZOJ3123: [Sdoi2013]森林
Description
小Z有一片森林,含有N個節點,每個節點上都有一個非負整數作為權值。初始的時候,森林中有M條邊。
小Z希望執行T個操作,操作有兩類:
1. Q x y k查詢點x到點y路徑上所有的權值中,第k小的權值是多少。此操作保證點x和點y連通,同時這兩個節點的路徑上至少有k個點。
2. L x y在點x和點y之間連接一條邊。保證完成此操作后,仍然是一片森林。
為了體現程序的在線性,我們把輸入數據進行了加密。設lastans為程序上一次輸出的結果,初始的時候lastans為0。
對于一個輸入的操作Q x y k,其真實操作為Q x^lastans y^lastans k^lastans。
對于一個輸入的操作L x y,其真實操作為L x^lastans y^lastans。其中^運算符表示異或,等價于pascal中的xor運算符。
請寫一個程序來幫助小Z完成這些操作。
對于所有的數據,n,m,T<=?8*10^48?104?.
Input
第一行包含一個正整數testcase,表示當前測試數據的測試點編號。保證1<=testcase<=20。
第二行包含三個整數N,M,T,分別表示節點數、初始邊數、操作數。
第三行包含N個非負整數表示 N個節點上的權值。
接下來 M行,每行包含兩個整數x和 y,表示初始的時候,點x和點y 之間有一條無向邊。
接下來 T行,每行描述一個操作,格式為”Q x y k“或者”L x y “,其含義見題目描述部分。
Output
對于每一個第一類操作,輸出一個非負整數表示答案。
Sample Input
18 4 8
1 1 2 2 3 3 4 4
4 7
1 8
2 4
2 1
Q 8 7 3 Q 3 5 1
Q 10 0 0
L 5 4
L 3 2 L 0 7
Q 9 2 5 Q 6 1 6
Sample Output
22
1
4
2
HINT?
對于第一個操作 Q 8 7 3,此時 lastans=0,所以真實操作為Q 8^0 7^0 3^0,也即Q 8 7 3。點8到點7的路徑上一共有5個點,其權值為4 1 1 2 4。
這些權值中,第三小的為 2,輸出 2,lastans變為2。
對于第二個操作 Q 3 5 1 ,此時lastans=2,所以真實操作為Q 3^2 5^2 1^2 ,也即Q 1 7 3。點1到點7的路徑上一共有4個點,其權值為 1 1 2 4 。
這些權值中,第三小的為2,輸出2,lastans變為 2。之后的操作類似。
題解Here!
首先看到第k小,立馬想到主席樹!
于是樹上第k小就這么愉快的解決了:?對?u,v,LCA(u,v),fa[ LCA(u,v) ]?整體處理——
int query(int u,int v,int f,int gf,int k,int l,int r){if(l==r)return l;int mid=l+r>>1,t=a[a[u].l].sum+a[a[v].l].sum-a[a[f].l].sum-a[a[gf].l].sum;if(k<=t)return query(a[u].l,a[v].l,a[f].l,a[gf].l,k,l,mid);else return query(a[u].r,a[v].r,a[f].r,a[gf].r,k-t,mid+1,r); }插入主席樹時,用父節點的主席樹更新此節點的主席樹——
void insert(int k,int l,int r,int &rt,int fa){int mid;rt=++size;a[rt]=a[fa];if(l==k&&k==r){a[rt].sum++;return;}mid=l+r>>1;if(k<=mid)insert(k,l,mid,a[rt].l,a[fa].l);else insert(k,mid+1,r,a[rt].r,a[fa].r);pushup(rt); }但是有個連邊操作很煩人。。。
兩個節點連邊,就是兩棵子樹的合并。
主席樹當然可以做到?O(logn)?時間內啟發式合并,但是LCA怎么辦?又不能啟發式。。。
等等,不能啟發式就暴力?dfs?啊!
時限?2s?,顯然可以卡一卡。。。
由于有修改,LCA當然選倍增。
于是,一個帶倍增處理的暴力?dfs?破空而出——
void buildtree(int rt,int fa,int ancestry){f[rt][0]=fa;for(int i=1;i<=19;i++)f[rt][i]=f[f[rt][i-1]][i-1];size[ancestry]++;deep[rt]=deep[fa]+1;father[rt]=fa;int x=lower_bound(num+1,num+p+1,val[rt])-num;ST::insert(x,1,p,root[rt],root[fa]);for(int i=head[rt];i;i=a[i].next){int will=a[i].to;if(will==fa)continue;buildtree(will,rt,ancestry);} }然后那個倍增?LCA?就不多說了。
注意:
1. 記得離散化!記得離散化!記得離散化!
2. 那個?testcase?是測試點編號!不是組數!也就是為了方便我們這些蒟蒻打打暴力。。。
3. 記得優化一下常數,什么?inline?啦,快速讀入啦亂搞一波。。。
附代碼:
#include<iostream> #include<algorithm> #include<cstdio> #define MAXN 80010 using namespace std; int n,m,q,p,c=1; int val[MAXN],num[MAXN],root[MAXN]; int head[MAXN],deep[MAXN],size[MAXN],f[MAXN][20],father[MAXN]; struct Edge{int next,to; }a[MAXN<<2]; inline int read(){int date=0,w=1;char c=0;while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}return date*w; } namespace ST{int size=0;struct Segment{int l,r,sum;}a[MAXN*400];inline void pushup(int rt){a[rt].sum=a[a[rt].l].sum+a[a[rt].r].sum;}inline void buildtree(){a[0].l=a[0].r=a[0].sum=root[0]=0;}void insert(int k,int l,int r,int &rt,int fa){int mid;rt=++size;a[rt]=a[fa];if(l==k&&k==r){a[rt].sum++;return;}mid=l+r>>1;if(k<=mid)insert(k,l,mid,a[rt].l,a[fa].l);else insert(k,mid+1,r,a[rt].r,a[fa].r);pushup(rt);}int query(int u,int v,int f,int gf,int k,int l,int r){if(l==r)return l;int mid=l+r>>1,t=a[a[u].l].sum+a[a[v].l].sum-a[a[f].l].sum-a[a[gf].l].sum;if(k<=t)return query(a[u].l,a[v].l,a[f].l,a[gf].l,k,l,mid);else return query(a[u].r,a[v].r,a[f].r,a[gf].r,k-t,mid+1,r);} } inline int find(int x){return father[x]==x?x:father[x]=find(father[x]);} inline void add(int x,int y){a[c].to=y;a[c].next=head[x];head[x]=c++;a[c].to=x;a[c].next=head[y];head[y]=c++; } void buildtree(int rt,int fa,int ancestry){f[rt][0]=fa;for(int i=1;i<=19;i++)f[rt][i]=f[f[rt][i-1]][i-1];size[ancestry]++;deep[rt]=deep[fa]+1;father[rt]=fa;int x=lower_bound(num+1,num+p+1,val[rt])-num;ST::insert(x,1,p,root[rt],root[fa]);for(int i=head[rt];i;i=a[i].next){int will=a[i].to;if(will==fa)continue;buildtree(will,rt,ancestry);} } int LCA(int x,int y){if(deep[x]<deep[y])swap(x,y);for(int i=19;i>=0;i--)if(deep[f[x][i]]>=deep[y])x=f[x][i];if(x==y)return x;for(int i=19;i>=0;i--)if(f[x][i]!=f[y][i]){x=f[x][i];y=f[y][i];}return f[x][0]; } void work(){char ch[2];int x,y,k,last=0;while(q--){scanf("%s",ch);x=read()^last;y=read()^last;if(ch[0]=='Q'){k=read()^last;int fa=LCA(x,y);last=num[ST::query(root[x],root[y],root[fa],root[f[fa][0]],k,1,p)];printf("%d\n",last);}if(ch[0]=='L'){add(x,y);int fx=find(x),fy=find(y);if(size[fx]<size[fy]){swap(x,y);swap(fx,fy);}buildtree(y,x,fx);}} } void init(){int x,y,cases=read();n=read();m=read();q=read();for(int i=1;i<=n;i++){num[i]=val[i]=read();father[i]=i;}for(int i=1;i<=m;i++){x=read();y=read();add(x,y);}sort(num+1,num+n+1);p=unique(num+1,num+n+1)-num-1;ST::buildtree();for(int i=1;i<=n;i++)if(!deep[i]){buildtree(i,0,i);father[i]=i;} } int main(){init();work();return 0; }?
轉載于:https://www.cnblogs.com/Yangrui-Blog/p/9085838.html
總結
以上是生活随笔為你收集整理的BZOJ3123: [Sdoi2013]森林的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 五大经典算法之动态规划
- 下一篇: python中with语句的使用