AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=3720
【题解】
这算是一道块状树的题目,细节看代码,代码中有详细注释
/*************
bzoj 3720
by chty
2016.11.23
*************/
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define FILE "read"
#define MAXN 60010
#define up(i,j,n) for(int i=j;i<=n;i++)
#define down(i,j,n) for(int i=j;i>=n;i--)
#define cmax(a,b) a=max(a,b)
#define cmin(a,b) a=min(a,b)
struct BLOCK{int a[210],size;}block[MAXN];
struct node{int y,next;}e[MAXN*2];
int n,m,len,ans,lim,cnt,Link[MAXN],head[MAXN],w[MAXN],belong[MAXN],f[MAXN];
namespace INIT{//输入
char buf[1<<15],*fs,*ft;
inline char getc() {return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
inline int read(){
int x=0,f=1; char ch=getc();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getc();}
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getc();}
return x*f;
}
}using namespace INIT;
namespace Block_Tree{//块状树
void insert(int x,int y){e[++len].next=head[x];head[x]=len;e[len].y=y;}
void Insert(int x,int v){
int temp;
for(temp=++block[x].size;temp>1&&block[x].a[temp-1]>v;temp--)//插入排序的过程
block[x].a[temp]=block[x].a[temp-1];
block[x].a[temp]=v;
}
int findmax(int x,int v){//二分查找块内严格大于x的最小的数
int l=1,r=block[x].size,temp=r+1;
while(l<=r){
int mid=(l+r)>>1;
if(block[x].a[mid]>v) {temp=mid; r=mid-1;}
else l=mid+1;
}
return temp;
}
int findmin(int x,int v){//二分查找块内小于等于x的最大的数
int l=1,r=block[x].size,temp=1;
while(l<=r){
int mid=(l+r)>>1;
if(block[x].a[mid]<=v) {temp=mid; l=mid+1;}
else r=mid-1;
}
return temp;
}
int Query(int x,int v){
//int temp=upper_bound(block[x].a+1,block[x].a+block[x].size+1,v)-block[x].a;
int temp=findmax(x,v);//二分查找块内严格大于x的最小的数,可以使用上面的stl代码,但是运行效率低
return block[x].size-temp+1;
}
void Dfs(int x,int v){ans+=Query(x,v);for(int i=head[x];i;i=e[i].next) Dfs(e[i].y,v);}
void Updata(int x,int v){
int p=belong[x];
//int temp=lower_bound(block[p].a+1,block[p].a+block[p].size+1,w[x])-block[p].a;
int temp=findmin(p,w[x]);//二分查找块内小于等于x的最大的数,可以使用上面的stl代码,但是运行效率低
for(;temp1&&block[p].a[temp-1]>v;temp--) block[p].a[temp]=block[p].a[temp-1];
block[p].a[temp]=v;
}
}
void insert(int x,int y){e[++len].next=Link[x];Link[x]=len;e[len].y=y;}
void dfs(int x){
if(block[belong[f[x]]].size==lim){//如果x的父结点所在的块已经达到上限,新开一个块,并把x放到其中
Block_Tree::Insert(belong[x]=++cnt,w[x]);
Block_Tree::insert(belong[f[x]],cnt);//维护块状树的结点关系
}
else Block_Tree::Insert(belong[x]=belong[f[x]],w[x]);//否则将x放在父结点所在块中
for(int i=Link[x];i;i=e[i].next) if(e[i].y!=f[x]) f[e[i].y]=x,dfs(e[i].y);//维护父亲,递归建树
}
void dfs(int x,int y){
if(w[x]>y) ans++;
for(int i=Link[x];i;i=e[i].next) if(e[i].y!=f[x]){
if(belong[e[i].y]==belong[x]) dfs(e[i].y,y);//当前结点不在一个整块内,暴力计算答案
else Block_Tree::Dfs(belong[e[i].y],y);//当前结点在一个整块内,在块内计算答案
}
}
void ask(int x,int y){
ans=0;
dfs(x,y);
printf("%d\n",ans);
}
void updata(int x,int y){
Block_Tree::Updata(x,y);
w[x]=y;
}
void add(int x,int y){
w[++n]=y; insert(x,n); f[n]=x;
if(block[belong[x]].size==lim) {
Block_Tree::Insert(belong[n]=++cnt,y);
Block_Tree::insert(belong[x],cnt);
}
else Block_Tree::Insert(belong[n]=belong[x],y);
}
void init(){
n=read(); up(i,1,n-1) {int x=read(),y=read(); insert(x,y); insert(y,x);}//建树
up(i,1,n) w[i]=read(); m=read();
lim=static_cast(sqrt(n*1.0)+1e-7);//确定块的大小
}
void solve(){
dfs(1);//建立块状树
up(i,1,m){
int flag=read(),x=read(),y=read();
x^=ans,y^=ans;
if(flag==0) ask(x,y);
else if(flag==1) updata(x,y);
else add(x,y);
}
}
int main(){
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
init();
solve();
return 0;
}