LCT練習

————學習LCT的同學建議去研究楊哲大神的《QTREE解法的一些研究》,自覺講得十分詳細。


LCT的核心算法也就是幾個過程:access.makeroot,link,cut……

搞出這幾個過程LCT也不是啥難寫的東西啦。

在最近做了幾道入門題,奉上。


spoj qtree3

樹上支持修改顏色(黑白),查詢路徑上第一個黑點編號。

直接LCT搞吧,記錄路徑上黑點的數量。修改時把x店轉到更直接改,查詢時在樹上走直到第一個黑點即可。


#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int Maxn=100005;
#define isroot(x) ((son[fa[x]][0]!=x) && (son[fa[x]][1]!=x))
#define update(x) sum[x]=sum[son[x][0]]+sum[son[x][1]]+flag[x]
int fa[Maxn],son[Maxn][2],rev[Maxn],sum[Maxn],flag[Maxn];
int n,q,i,K,x,y;

void push(int x){
  swap(son[x][0],son[x][1]);
  if (son[x][0]>0)
    rev[son[x][0]]^=1;
  if (son[x][1]>0)
    rev[son[x][1]]^=1;
  rev[x]=0;
}

void rotate(int x,int f,int K){
  if (!isroot(f)){
  	if (son[fa[f]][0]==f) son[fa[f]][0]=x;
  	  else son[fa[f]][1]=x;
  }
  fa[x]=fa[f];
  
  if (son[x][K^1]>0)
    fa[ son[x][K^1] ]=f;
  son[f][K]=son[x][K^1];
  son[x][K^1]=f; fa[f]=x;
  update(f);
}

void Splay(int x){
  int f, gf;
  while (!isroot(x)){
 	f=fa[x]; gf=fa[f];
 	if (rev[gf]) push(gf);
 	if (rev[f]) push(f);
 	if (rev[x]) push(x);
 	if (isroot(f)){
 	  if (son[f][0]==x) rotate(x,f,0);
 	  if (son[f][1]==x) rotate(x,f,1);
 	} else
 	{
 	  if (son[f][0]==x && son[gf][0]==f) rotate(f,gf,0), rotate(x,f,0);
 	  if (son[f][1]==x && son[gf][1]==f) rotate(f,gf,1), rotate(x,f,1);
 	  if (son[f][0]==x && son[gf][1]==f) rotate(x,f,0), rotate(x,gf,1);
 	  if (son[f][1]==x && son[gf][0]==f) rotate(x,f,1), rotate(x,gf,0);
 	}
  }
  if (rev[x]) push(x);
  update(x);
}

void access(int x){
  int t=0;
  while (x){
  	Splay(x);
  	son[x][1]=t;
  	update(x);
  	t=x; x=fa[x];
  }
}

void makeroot(int x)
{ access(x); Splay(x); rev[x]^=1; }

void Link(int x,int y)
{ makeroot(x); fa[x]=y; access(x);}

void cut(int x,int y){
  makeroot(x); access(y); Splay(y);
  fa[son[y][0]]=0; son[y][0]=0;
  Splay(x); Splay(y);
}

int main(){
  freopen("qtree3.in","r",stdin);
  freopen("qtree3.out","w",stdout);
  scanf("%d%d",&n,&q);
  for (i=1;i<n;i++){
    scanf("%d%d",&x,&y);
    Link(x,y);
  }
  for (i=1;i<=q;i++){
  	scanf("%d%d",&K,&x);
  	if (K==0){
  	  makeroot(x);
  	  flag[x]^=1;
  	  update(x);
  	} else
  	{
  	  makeroot(1);
  	  access(x);
  	  Splay(x);
  	  if (sum[x]==0) {puts("-1");continue;}
  	  while (sum[x]>0){
  	  	if (rev[x]) push(x);
  	  	if (sum[son[x][0]]>0) x=son[x][0];
  	  	  else if (flag[x]==1) break;
  	  	    else x=son[x][1];
  	  }
  	  printf("%d\n",x);
  	}
  }
  return 0;
}



spoj gss7

樹上支持修改整個路徑上的值,詢問路徑上最大子序列和。

LCT+標記大法!像gss1一樣維護最大子序列和,把它套到LCT裏就好了

//注意標記的傳遞順序。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int Maxn=100005;
#define isroot(x) ((son[fa[x]][0]!=x)&&(son[fa[x]][1]!=x))
int son[Maxn][2],fa[Maxn],rev[Maxn],size[Maxn],sum[Maxn];
int tip[Maxn],lef[Maxn],rig[Maxn],maxx[Maxn],w[Maxn];
int n,m,i,x,y,z,K;

void push1(int x){
  swap(son[x][0],son[x][1]);
  //swap(lef[x],rig[x]);
  if (son[x][0]>0){
    rev[son[x][0]]^=1;
    swap(lef[son[x][0]],rig[son[x][0]]);
  }
  if (son[x][1]>0){
    rev[son[x][1]]^=1;
    swap(lef[son[x][1]],rig[son[x][1]]);
  }
  rev[x]=0;
}

void push2(int x){
  int y;
  if (son[x][0]>0){
  	y=son[x][0];
  	w[y]=tip[y]=tip[x];
  	sum[y]=w[y]*size[y];
  	if (tip[y]<0) maxx[y]=lef[y]=rig[y]=0;
  	  else maxx[y]=lef[y]=rig[y]=tip[y]*size[y];
  }
  if (son[x][1]>0){
  	y=son[x][1];
  	w[y]=tip[y]=tip[x];
  	sum[y]=w[y]*size[y];
  	if (tip[y]<0) maxx[y]=lef[y]=rig[y]=0;
  	  else maxx[y]=lef[y]=rig[y]=tip[y]*size[y];
  }
  tip[x]=-10001;
}

void update(int x){
  size[x]=size[son[x][0]]+size[son[x][1]]+1;
  sum[x]=sum[son[x][0]]+sum[son[x][1]]+w[x];
  lef[x]=max(lef[son[x][0]],sum[son[x][0]]+w[x]+lef[son[x][1]]);
  rig[x]=max(rig[son[x][1]],sum[son[x][1]]+w[x]+rig[son[x][0]]);
  maxx[x]=max(maxx[son[x][0]],maxx[son[x][1]]);
  maxx[x]=max(maxx[x],rig[son[x][0]]+w[x]+lef[son[x][1]]);
}

void rotate(int x,int f,int K){
  if (!isroot(f)){
  	if (son[fa[f]][0]==f) son[fa[f]][0]=x;
  	  else son[fa[f]][1]=x;
  }
  fa[x]=fa[f];
  
  if (son[x][K^1]>0)
    fa[son[x][K^1]]=f;
  son[f][K]=son[x][K^1];
  fa[f]=x; son[x][K^1]=f;
  update(f);
}

void Splay(int x){
  int f, gf;
  while (!isroot(x)){
  	f=fa[x]; gf=fa[f];
  	if (rev[gf]>0) push1(gf);
  	if (rev[f]>0) push1(f);
  	if (rev[x]>0) push1(x);
  	if (tip[gf]>-10001) push2(gf);
  	if (tip[f]>-10001) push2(f);
  	if (tip[x]>-10001) push2(x);
  	if (isroot(f)){
  	  if (son[f][0]==x) rotate(x,f,0);
  	  if (son[f][1]==x) rotate(x,f,1);
  	} else
  	{
  	  if (son[f][0]==x && son[gf][0]==f) rotate(f,gf,0), rotate(x,f,0);
  	  if (son[f][1]==x && son[gf][1]==f) rotate(f,gf,1), rotate(x,f,1);
  	  if (son[f][0]==x && son[gf][1]==f) rotate(x,f,0), rotate(x,gf,1);
  	  if (son[f][1]==x && son[gf][0]==f) rotate(x,f,1), rotate(x,gf,0);
  	}
  }
  if (rev[x]>0) push1(x);
  if (tip[x]>-10001) push2(x);
  update(x);
}

void access(int x){
  int t=0;
  while (x){
  	Splay(x);
  	son[x][1]=t;
  	update(x);
    t=x; x=fa[x];
  }
}

void makeroot(int x)
{ access(x); Splay(x); rev[x]^=1; swap(lef[x],rig[x]); }

void Link(int x,int y)
{ makeroot(x); fa[x]=y; access(x); }

void cut(int x,int y){
  makeroot(x); access(y); Splay(y); 
  fa[ son[y][0] ]=0;  son[y][0]=0;
  Splay(x); Splay(y);
}

int main(){
  freopen("gss7.in","r",stdin);
  freopen("gss7.out","w",stdout);
  scanf("%d",&n);
  for (i=1;i<=n;i++){
    scanf("%d",&w[i]);
    size[i]=1;
    if (w[i]<0) continue;
    sum[i]=lef[i]=rig[i]=maxx[i]=w[i];
  }
  memset(tip,-10,sizeof(tip));
  for (i=1;i<n;i++){
  	scanf("%d%d",&x,&y);
  	Link(x, y);
  }
  scanf("%d",&m);
  for (i=1;i<=m;i++){
  	scanf("%d",&K);
  	if (K==1){
  	  scanf("%d%d",&x,&y);
  	  makeroot(x);
	  access(y);
	  Splay(y);
	  printf("%d\n",maxx[y]);
  	} else
  	{
  	  scanf("%d%d%d",&x,&y,&z);
  	  makeroot(x);
  	  access(y);
  	  Splay(y);
  	  w[y]=tip[y]=z;
  	  sum[y]=z*size[y];
  	  if (z<0) maxx[y]=lef[y]=rig[y]=0;
  	    else maxx[y]=lef[y]=rig[y]=z*size[y];
  	}
  }
  return 0;
}

noi2014 day1 T2 魔法森林

//各種騙分ac的算法就不說了。。。。

這題正解是LCT。

將邊按a值從小到大排序,順序掃描加入,維護MST。

爲了方便利用LCT,我們把每條邊看作一個虛點,其權值爲這條邊的b值,他與原圖中的x,y相連,原圖中的點權值就記爲0吧。

每次加入一條邊,如果x,y還未聯通,直接把他們連起來,否則看x,y這條鏈上的最大值是否大於這條邊的權值,去較小的保留。

每加完一條邊求出a+(1~n的最大值),取其中最小值即爲答案。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
#define isroot(x) ((son[fa[x]][0]!=x)&&(son[fa[x]][1]!=x))
#define update(x) maxx[x]=max(w[x], max(maxx[son[x][0]],maxx[son[x][1]]) )
const int Maxn=200005;
int w[Maxn],rev[Maxn],son[Maxn][2],fa[Maxn];
int maxx[Maxn],F[Maxn],n,m,ans,i,t,m1,m2;
struct EDGE
{
  int x,y,a,b;
  void read(){ scanf("%d%d%d%d",&x,&y,&a,&b); }
  bool operator <(const EDGE &r)const
    { return a<r.a;}
}e[Maxn];

void push(int x){
  swap(son[x][0],son[x][1]);
  if (son[x][0]>0) rev[son[x][0]]^=1;
  if (son[x][1]>0) rev[son[x][1]]^=1;
  rev[x]=0;
}

void rotate(int x,int f,int K){
  if (!isroot(f)){
  	if (son[fa[f]][0]==f) son[fa[f]][0]=x;
      else son[fa[f]][1]=x;
  }
  fa[x]=fa[f];
  
  if (son[x][K^1]>0)
    fa[ son[x][K^1] ]=f;
  son[f][K]=son[x][K^1];
  son[x][K^1]=f; fa[f]=x;
  update(f);
}

void Splay(int x){
  int f, gf;
  while (!isroot(x)){
  	f=fa[x]; gf=fa[f];
  	if (rev[gf]>0) push(gf);
  	if (rev[f]>0) push(f);
  	if (rev[x]>0) push(x);
  	if (isroot(f)){
  	  if (son[f][0]==x) rotate(x,f,0);
  	  if (son[f][1]==x) rotate(x,f,1);
  	} else
  	{
  	  if (son[f][0]==x && son[gf][0]==f) rotate(f,gf,0), rotate(x,f,0);
  	  if (son[f][1]==x && son[gf][1]==f) rotate(f,gf,1), rotate(x,f,1);
  	  if (son[f][0]==x && son[gf][1]==f) rotate(x,f,0), rotate(x,gf,1);
  	  if (son[f][1]==x && son[gf][0]==f) rotate(x,f,1), rotate(x,gf,0);
  	}
  }
  if (rev[x]>0) push(x);
  update(x);
}

void access(int x){
  int t=0;
  while (x){
  	Splay(x);
  	son[x][1]=t;
  	update(x);
  	t=x; x=fa[x];
  }
}

void makeroot(int x)
 { access(x); Splay(x); rev[x]^=1; }

void Link(int x,int y)
 { makeroot(x); fa[x]=y; access(x); }

void cut(int x,int y){
  makeroot(x); access(y); Splay(y);
  fa[ son[y][0] ]=0; son[y][0]=0;
  Splay(x); Splay(y);
}

int getit(int x){
  int d=maxx[x];
  while (true){
  	if (rev[x]>0) push(x);
  	if (w[x]==d) break;
  	if (maxx[son[x][0]]==d) x=son[x][0];
  	  else x=son[x][1];
  }
  return x-n;
}

int findf(int x){
  int xx=x, xxx;
  while (xx!=F[xx]) xx=F[xx];
  while (x!=xx) xxx=x, x=F[x], F[xxx]=xx;
  return xx;
}

int main(){
  //freopen("forest.in","r",stdin);
  //freopen("forest.out","w",stdout);
  scanf("%d%d",&n,&m);
  for (i=1;i<=m;i++)
  	e[i].read();
  sort(e+1,e+m+1);
  for (i=1;i<=m;i++)
    maxx[i+n]=w[i+n]=e[i].b;
  ans=(1<<30);
  for (i=1;i<=n;i++) F[i]=i;
  for (i=1;i<=m;i++){
  	m1=findf(e[i].x);
  	m2=findf(e[i].y);
  	if (m1!=m2){
  	  F[m1]=m2;
  	  Link(e[i].x,i+n);
      Link(e[i].y,i+n);
  	} else{
  	  makeroot(e[i].x);
  	  access(e[i].y);
  	  Splay(e[i].x);
  	  if (maxx[e[i].x]>e[i].b){
  	    t=getit(e[i].x);
  	    cut(e[t].x,t+n);
  	    cut(e[t].y,t+n);
  	    Link(e[i].x,i+n);
  	    Link(e[i].y,i+n);
  	  }
    }
    
    m1=findf(1); m2=findf(n);
    if (m1==m2){
   	  makeroot(1);
   	  access(n);
   	  Splay(1);
   	  if (ans>e[i].a+maxx[1])
   	    ans=e[i].a+maxx[1];
    }
  }
  if (ans==(1<<30)) ans=-1;
  printf("%d\n",ans);
  return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章