[SDOI2013]森林
写得非常辛苦的一道题了。本来这道题其实并不难,思想理解起来也很容易,但由于我有些细节处没有搞清楚,白白浪费了一个上午来调这道题。
这道题说白了就是树上主席树(也算是树套树了?)的模板。考虑每个点维护从根到该点这条路径上的信息,查询路径时把路径拆成两个部分再差分就可以了。由于本题要支持连边,所以还需要用启发式合并来做。另外,树上主席树(动态)要设置一个0号节点,然后默认每个节点都是它的孩子;不然每个节点都建一棵权值线段树还不如打暴力。复杂度很不错,数组开到左移7位就够了。
就这么一个简单至极的东西我竟然花了两个多小时,郁闷想哭。
#include<cstdio>
#include<algorithm>
//#define zczc
using namespace std;
const int N=80010;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
inline bool op(){
char w=getchar();
while(w!='Q'&&w!='L')w=getchar();
return w=='Q';
}
inline void swap(int &s1,int &s2){
int s3=s1;s1=s2;s2=s3;return;
}
int m,n,q,root[N];
int num,data[N],sa[N];
namespace bc{//并查集
int f[N],size[N];
void init(){
for(int i=1;i<=m;i++)f[i]=i,size[i]=1;
}
inline int find(int wh){
return f[wh]==wh?wh:f[wh]=find(f[wh]);
}
inline void merge(int x,int y){
int fx=find(x),fy=find(y);
if(size[fx]<size[fy])swap(fx,fy);
f[fy]=fx,size[fx]+=size[fy];
}
}
namespace t{//主席树
struct node{
int lc,rc,l,r,size;
}t[N<<7],newone;
int top,st[N<<8],cnt;
int kk(){
return top?st[top--]:++cnt;
}
int kill(int wh){
st[++top]=wh;t[wh]=newone;
}
void init(){
for(int i=1;i<=cnt;i++)t[i]=newone;
cnt=0;
}
int build(int l,int r){
int wh=++cnt;t[wh].l=l,t[wh].r=r;int mid=l+r>>1;
if(l==r)return wh;
t[wh].lc=build(l,mid);t[wh].rc=build(mid+1,r);
return wh;
}
int insert(int x,int pl){
int wh=++cnt;t[wh]=t[x];t[wh].size++;
if(t[wh].l==t[wh].r)return wh;int mid=t[x].l+t[x].r>>1;
if(pl<=mid)t[wh].lc=insert(t[x].lc,pl);
else t[wh].rc=insert(t[x].rc,pl);return wh;
}
int work(int x,int y,int f,int ff,int want){
if(t[x].l==t[x].r)return sa[t[x].l];int mid=t[x].l+t[x].r>>1,lr=t[x].l;
int sum=t[t[x].lc].size+t[t[y].lc].size-2*t[t[f].lc].size+(data[ff]<=mid&&data[ff]>=t[x].l);
if(sum>=want)return work(t[x].lc,t[y].lc,t[f].lc,ff,want);
else return work(t[x].rc,t[y].rc,t[f].rc,ff,want-sum);
}
}
namespace tr{//树
struct edge{
int t,next;
}e[N<<2];
int esum,head[N];
inline void add(int fr,int to){
e[++esum]=(edge){to,head[fr]};head[fr]=esum;
}
int lg[N],nxt[N][20],d[N];
void init(){
esum=0;lg[0]=1;
for(int i=0;i<=m;i++)lg[i]=lg[i>>1]+1,d[i]=1;
d[0]=-1;
}
void dfs(int wh,int fa){
d[wh]=d[fa]+1;nxt[wh][0]=fa;
for(int i=1;i<=lg[d[wh]];i++)nxt[wh][i]=nxt[nxt[wh][i-1]][i-1];
root[wh]=t::insert(root[fa],data[wh]);
for(int i=head[wh],th;i;i=e[i].next){
if((th=e[i].t)==fa)continue;dfs(th,wh);
}
}
int lca(int x,int y){
if(d[x]<d[y])swap(x,y);
for(int i=lg[d[x]];i>=0;i--){
if(d[nxt[x][i]]>=d[y])x=nxt[x][i];
}
if(x==y)return x;
for(int i=lg[d[x]];i>=0;i--){
if(nxt[x][i]!=nxt[y][i])x=nxt[x][i],y=nxt[y][i];
}
return nxt[x][0];
}
}
void worknum(){
for(int i=1;i<=m;i++)sa[i]=data[i];
sort(sa+1,sa+m+1);
num=unique(sa+1,sa+m+1)-sa-1;
for(int i=1;i<=m;i++)data[i]=lower_bound(sa+1,sa+num+1,data[i])-sa;
return;
}
void makelink(int x,int y){
int fx=bc::find(x),fy=bc::find(y);
if(bc::size[fx]<bc::size[fy])swap(x,y);
bc::merge(x,y);tr::add(x,y);tr::add(y,x);tr::dfs(y,x);
return;
}
void enter(){
int s1,s2,s3,lans=0;
read(m);read(n);read(q);tr::d[0]=-1;
bc::init();t::init();tr::init();
for(int i=1;i<=m;i++)read(data[i]);
worknum();root[0]=t::build(1,num);
for(int i=1;i<=m;i++)root[i]=t::insert(root[0],data[i]);
while(n--){read(s1);read(s2);makelink(s1,s2);}
for(int sss=1;sss<=q;sss++){
if(op()){
read(s1);read(s2);read(s3);s1^=lans,s2^=lans,s3^=lans;
int ll=tr::lca(s1,s2);
printf("%d\n",lans=t::work(root[s1],root[s2],root[ll],ll,s3));
}
else{
read(s1);read(s2);s1^=lans;s2^=lans;
makelink(s1,s2);
}
}
}
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
int test;
read(test);
enter();
return 0;
}
一如既往,万事胜意