题目大意:给出一个森林,每个节点都有一个权值。有若干加边操作,问两点之间路径上的第k小权值是多少。
思路:这题和COT1比较像,但是多了连接操作。这样就只能暴力合并连个树。启发式合并会保证时间复杂度不至于太大。然后就是用可持久化线段树维护一个树的信息,按照dfs序来建树,每个节点的可持久化链的参考版本就是它父亲的版本。之后利用权值线段树可区间加减的特性,用f[x] + f[y] - f[lca] - f[father[lca]]来计算权值。
CODE:
#include
#include
#include
#include
#define MAX 80010
#define MAX_RANGE 1000000000
#define POOL_SIZE 20000000
using namespace std;
struct PersSegTree{
PersSegTree *son[2];
int num;
}mempool[POOL_SIZE],*C = mempool;
int points,edges,asks;
int src[MAX];
int head[MAX],total;
int next[MAX << 1],aim[MAX << 1];
int f[MAX],cnt[MAX];
int xx[MAX],top,father[MAX][20];
int deep[MAX];
PersSegTree *tree[MAX];
char s[10];
PersSegTree *NewNode(PersSegTree *_,PersSegTree *__,int ___)
{
C->son[0] = _;
C->son[1] = __;
C->num = ___;
return C++;
}
void Initialize()
{
for(int i = 1;i <= points; ++i)
f[i] = i,cnt[i] = 1;
tree[0] = NewNode(C,C,0);
}
inline void Add(int x,int y)
{
next[++total] = head[x];
aim[total] = y;
head[x] = total;
}
int Find(int x)
{
if(f[x] == x) return x;
return f[x] = Find(f[x]);
}
void Unite(int x,int y)
{
int fx = Find(x);
int fy = Find(y);
cnt[fx] += cnt[fy];
f[fy] = fx;
}
void SparseTable()
{
for(int j = 1;j <= 19; ++j)
for(int i = 1;i <= top; ++i)
father[xx[i]][j] = father[father[xx[i]][j - 1]][j - 1];
}
int GetLCA(int x,int y)
{
if(deep[x] < deep[y]) swap(x,y);
for(int i = 19; ~i; --i)
if(deep[father[x][i]] >= deep[y])
x = father[x][i];
if(x == y) return x;
for(int i = 19; ~i; --i)
if(father[x][i] != father[y][i])
x = father[x][i],y = father[y][i];
return father[x][0];
}
PersSegTree *BuildTree(PersSegTree *consult,int l,int r,int val)
{
if(l == r) return NewNode(NULL,NULL,consult->num + 1);
int mid = (l + r) >> 1;
if(val <= mid)
return NewNode(BuildTree(consult->son[0],l,mid,val),consult->son[1],consult->num + 1);
else
return NewNode(consult->son[0],BuildTree(consult->son[1],mid + 1,r,val),consult->num + 1);
}
void DFS(int x,int last)
{
deep[x] = deep[last] + 1;
father[x][0] = last;
xx[++top] = x;
tree[x] = BuildTree(tree[last],0,MAX_RANGE,src[x]);
for(int i = head[x];i;i = next[i]) {
if(aim[i] == last) continue;
DFS(aim[i],x);
}
}
int GetKth(PersSegTree *_l,PersSegTree *_r,PersSegTree *f,PersSegTree *p,int l,int r,int k)
{
if(l == r) return l;
int mid = (l + r) >> 1;
int temp = _l->son[0]->num + _r->son[0]->num - f->son[0]->num - p->son[0]->num;
if(k <= temp) return GetKth(_l->son[0],_r->son[0],f->son[0],p->son[0],l,mid,k);
return GetKth(_l->son[1],_r->son[1],f->son[1],p->son[1],mid + 1,r,k - temp);
}
int main()
{
scanf("%*d%d%d%d",&points,&edges,&asks);
Initialize();
for(int i = 1;i <= points; ++i)
scanf("%d",&src[i]);
for(int x,y,i = 1;i <= edges; ++i) {
scanf("%d%d",&x,&y);
Add(x,y),Add(y,x);
Unite(x,y);
}
for(int i = 1;i <= points; ++i)
if(!deep[i]) DFS(i,0);
SparseTable();
int last_ans = 0;
for(int x,y,z,i = 1;i <= asks; ++i) {
scanf("%s%d%d",s,&x,&y);
x ^= last_ans,y ^= last_ans;
if(s[0] == 'Q') {
scanf("%d",&z);
z ^= last_ans;
int lca = GetLCA(x,y);
printf("%d\n",last_ans = GetKth(tree[x],tree[y],tree[lca],tree[father[lca][0]],0,MAX_RANGE,z));
}
else {
int fx = Find(x);
int fy = Find(y);
if(cnt[fx] > cnt[fy]) swap(x,y);
top = 0;
DFS(x,y);
Unite(x,y);
Add(x,y),Add(y,x);
SparseTable();
}
}
return 0;
}