Query on a tree IV(SPOJ - QTREE4)
題目描述:
You are given a tree (an acyclic undirected connected graph) with N nodes, and nodes numbered 1,2,3...,N. Each edge has an integer value assigned to it(note that the value can be negative). Each node has a color, white or black. We define dist(a, b) as the sum of the value of the edges on the path from node a to node b.
All the nodes are white initially.
We will ask you to perfrom some instructions of the following form:
- C a : change the color of node a.(from black to white or from white to black)
- A : ask for the maximum dist(a, b), both of node a and node b must be white(a can be equal to b). Obviously, as long as there is a white node, the result will alway be non negative.
給出一棵n個結點的樹,每條邊有邊權。一開始,每個點都是白色的。
再給出q個詢問,每次詢問有兩種操作:
1.C a 表示改變a結點的顏色,黑變白,白變黑。
2.A 表示詢問樹上某兩個白點(可以是同一個點)間最遠距離
註意原題卡常,可以嘗試換用clang編譯器提交。
思路:
QTree 5 的進化版。。(為甚是這個順序我也不清楚。。)
主要操作和QTree4的操作差不多,詳細內容可見那題題解:Query on a tree V
上一題的思想是在每個點開一個可刪堆\(Q1\),然後利用點分樹優化查詢與更新,這次其實也差不多只不過它並沒有給你確定其中一個點,那麽接下來的操作就比較騷有趣了
為了避免多次枚舉,我們在每個點上多開一個隊列\(Q2\)
所以代碼中最關鍵的兩點就是對於這三種隊列的更新了!仔細理解。
最後就是把每個點的最大值存在最後一個隊列\(A\)中,同理每次一個點的\(Q2\)中的最大兩值和改變時,都要在\(A\)中進行對應的更新(刪除舊值,加入新值),那麽最終查詢答案只要返回\(A\)的\(Top\)即可,至於沒有白點的判斷,只要維護一個\(tot\),計數白點數量,不必多說!
還有一個需要說的就是點分樹的存法,由於這題時限,不能用常規\(log\)求\(LCA\)來算距離,可以全部預處理出來存一個正向表(或\(vector\))中。存每個兒子的每個父親以及距離,這些均可以在一個\(dfs\)中求出
代碼
#include<cstdio>
#include<cstring>
#include<queue>
#define FOR(i,l,r) for(register int i=l,END=r;i<=END;i++)
#define DOR(i,r,l) for(register int i=r,END=l;i>=END;i--)
#define loop(i,n) for(register int i=0,END=n;i<END;i++)
#define mms(a,x) memset(a,x,sizeof a)
#define sf scanf
#define pf printf
using namespace std;
const int N=1e5+5,INF=1e9;
int n;
struct Graph{
int tot,to[N*20],nxt[N*20],len[N*20],head[N];
void add(int x,int y,int z){tot++;to[tot]=y;nxt[tot]=head[x];len[tot]=z;head[x]=tot;}
void clear(){mms(head,-1);tot=0;}
#define EOR(A,i,x) for(register int i=A.head[x];i!=-1;i=A.nxt[i])
}G,G2;//原樹和點分樹的路徑
struct Heap{//可刪堆
priority_queue<int>Q,del;
void Push(int x){if(x!=-INF)Q.push(x);}
void Del(int x){if(x!=-INF)del.push(x);}
void upd(){while(!del.empty()&&Q.top()==del.top())Q.pop(),del.pop();}
int Size(){return Q.size()-del.size();}
int Top(){upd();if(Q.empty())return -INF;return Q.top();}
int Sum2(){
upd();int siz=Size();
if(!siz)return -INF;
if(siz==1)return 0;
int mx1=Top();Q.pop();
int mx2=Top();Q.push(mx1);
return max(mx1+mx2,0);
}
}A,B[N],C[N];//C便是題解中的Q1,B便是題解中的Q2
bool col[N];//記錄顏色
struct DAC_Tree{//點分樹
bool vis[N];
int sz[N],mx[N],fa[N];
int t_sz,center;
void get_dis(int x,int f,int dis,int center){//預處理上面的G2
G2.add(x,center,dis);
EOR(G,i,x){
int v=G.to[i];
if(v==f||vis[v])continue;
get_dis(v,x,dis+G.len[i],center);
}
}
void get_center(int x,int f){//尋找重心
sz[x]=1,mx[x]=0;
EOR(G,i,x){
int v=G.to[i];
if(v==f||vis[v])continue;
get_center(v,x);
sz[x]+=sz[v];
mx[x]=max(mx[x],sz[v]);
}
mx[x]=max(mx[x],t_sz-sz[x]);
if(!center||mx[center]>mx[x])center=x;
}
void DAC(int x){//點分治
vis[x]=1;
G2.add(x,x,0);
EOR(G,i,x){
int v=G.to[i];
if(vis[v])continue;
get_dis(v,x,G.len[i],x);
center=0,t_sz=sz[v];
get_center(v,x);
fa[center]=x;
DAC(center);
}
}
//以下兩個函數為本題關鍵,註意三個隊列的更新
void insert(int x){//把一個點變白
int sum=B[x].Sum2();
B[x].Push(0);
int nsum=B[x].Sum2();
if(nsum!=sum){
A.Del(sum);
A.Push(nsum);
}
EOR(G2,i,x){
if(G2.nxt[i]==-1)break;
int v=G2.to[i],f=G2.to[G2.nxt[i]],dis=G2.len[G2.nxt[i]],tp=C[v].Top();
C[v].Push(dis);
int ntp=C[v].Top();
if(ntp!=tp){
sum=B[f].Sum2();
B[f].Del(tp);
B[f].Push(dis);
nsum=B[f].Sum2();
if(nsum!=sum){
A.Del(sum);
A.Push(nsum);
}
}
}
}
void erase(int x){//把一個點變黑
int sum=B[x].Sum2();
B[x].Del(0);
int nsum=B[x].Sum2();
if(nsum!=sum){
A.Del(sum);
A.Push(nsum);
}
EOR(G2,i,x){
if(G2.nxt[i]==-1)break;
int v=G2.to[i],dis=G2.len[G2.nxt[i]],f=G2.to[G2.nxt[i]],tp=C[v].Top();
C[v].Del(dis);
int ntp=C[v].Top();
if(ntp!=tp){
sum=B[f].Sum2();
if(tp!=-INF)B[f].Del(tp);
B[f].Push(C[v].Top());
nsum=B[f].Sum2();
if(nsum!=sum){
A.Del(sum);
A.Push(nsum);
}
}
}
}
//預處理
void init(){
center=0,t_sz=n;
get_center(1,0);
DAC(center);
FOR(i,1,n)insert(i);
}
}Tr;
int tot;
int main(){
G.clear(),G2.clear();
sf("%d",&n);
loop(i,n-1){
int x,y,z;
sf("%d%d%d",&x,&y,&z);
G.add(x,y,z),G.add(y,x,z);
}
Tr.init();
int q;tot=n;
sf("%d",&q);
while(q--){
char op[5];
sf("%s",op);
if(op[0]=='A'){
if(!tot)pf("They have disappeared.\n");//無白點
else pf("%d\n",A.Top());
}
else {
int a;sf("%d",&a);
col[a]^=1;
if(col[a]){Tr.erase(a);tot--;}
else {Tr.insert(a);tot++;}
}
}
return 0;
}
Query on a tree IV(SPOJ - QTREE4)