BZOJ3123: [Sdoi2013]森林
题解:在树上转移主席树 每次连边暴力倍增维护 每个节点维护到根路径上的权值情况即可
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <stack> #include <queue> #include <cmath> #include <set> #include <map> #define mp make_pair #define pb push_back #define pii pair<int,int> #define link(x) for(edge *j=h[x];j;j=j->next) #define inc(i,l,r) for(int i=l;i<=r;i++) #define dec(i,r,l) for(int i=r;i>=l;i--) const int MAXN=8e4+10; const double eps=1e-8; #define ll long long using namespace std; struct edge{int t;edge*next;}e[MAXN<<1],*h[MAXN],*o=e; void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;} ll read(){ ll x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } int fa[MAXN],f[MAXN][21],sz,num[MAXN],cnt,a[MAXN],n,m,q; int rt[MAXN],size[MAXN]; bool vis[MAXN]; int find1(int x){ if(x==fa[x])return x; else return fa[x]=find1(fa[x]); } typedef struct node{ int l,r,sum; }node; node d[MAXN*404]; vector<int>vec; void update(int &x,int y,int l,int r,int t){ x=++cnt;d[x]=d[y];d[x].sum++; //cout<<l<<" "<<r<<" "<<t<<endl; if(l==r)return ; int mid=(l+r)>>1; if(t<=mid)update(d[x].l,d[y].l,l,mid,t); else update(d[x].r,d[y].r,mid+1,r,t); } int ans;int dep[MAXN]; void querty(int x,int y,int t1,int t2,int l,int r,int k){ if(l==r){ans=l;return ;} int mid=(l+r)>>1; int t=d[d[x].l].sum+d[d[y].l].sum-d[d[t1].l].sum-d[d[t2].l].sum; if(k<=t)querty(d[x].l,d[y].l,d[t1].l,d[t2].l,l,mid,k); else querty(d[x].r,d[y].r,d[t1].r,d[t2].r,mid+1,r,k-t); } void dfs(int x,int pre,int deep){ //cout<<x<<"====="<<endl; num[x]=1;f[x][0]=pre;vis[x]=1;dep[x]=deep+1; link(x){ if(j->t!=pre)dfs(j->t,x,deep+1),num[x]+=num[j->t]; } } void dfs1(int x){ //cout<<x<<"===="<<endl; update(rt[x],rt[f[x][0]],1,sz,a[x]); for(int i=1;i<=20;i++)f[x][i]=f[f[x][i-1]][i-1]; link(x){ if(j->t!=f[x][0])dfs1(j->t); } } int Lca(int u,int v){ if(dep[u]<dep[v])swap(u,v); int tmp=dep[u]-dep[v]; for(int i=0;i<=20;i++)if(tmp&(1<<i))u=f[u][i]; if(u==v)return u; for(int i=20;i>=0;i--){ if(f[u][i]!=f[v][i])u=f[u][i],v=f[v][i]; } return f[u][0]; } int main(){ read();n=read();m=read();q=read(); inc(i,1,n)fa[i]=i,a[i]=read(),vec.pb(a[i]); sort(vec.begin(),vec.end()); sz=unique(vec.begin(),vec.end())-vec.begin(); inc(i,1,n)a[i]=lower_bound(vec.begin(),vec.begin()+sz,a[i])-vec.begin()+1; int u,v; inc(i,1,m){ u=read();v=read();add(u,v);add(v,u); int t1=find1(u);int t2=find1(v); if(t1<t2)swap(t1,t2); fa[t1]=t2; } inc(i,1,n)if(!vis[i])dfs(i,0,0),dfs1(i); inc(i,1,n)size[i]=num[i]; char ch;int res=0,k; while(q--){ scanf(" %c",&ch);u=read()^res;v=read()^res; //cout<<u<<" "<<v<<endl; if(ch=='L'){ int t1=find1(u);int t2=find1(v); if(size[t1]>size[t2])swap(t1,t2),swap(u,v); fa[t1]=t2;size[t2]+=size[t1];dfs(u,v,dep[v]);dfs1(u); add(u,v);add(v,u); } else{ k=read()^res;int lca=Lca(u,v); querty(rt[u],rt[v],rt[lca],rt[f[lca][0]],1,sz,k);res=vec[ans-1]; printf("%d\n",res); } } return 0; }
3123: [Sdoi2013]森林
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 4658 Solved: 1371
[Submit][Status][Discuss]
Description
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
1
8 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
8 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
2
2
1
4
2
2
1
4
2