HYSBZ 3991 寻宝游戏
題意:
小B最近正在玩一個尋寶游戲,這個游戲的地圖中有N個村莊和N-1條道路,并且任何兩個村莊之間有且僅有一條路徑可達。游戲開始時,玩家可以任意選擇一個村莊,瞬間轉(zhuǎn)移到這個村莊,然后可以任意在地圖的道路上行走,若走到某個村莊中有寶物,則視為找到該村莊內(nèi)的寶物,直到找到所有寶物并返回到最初轉(zhuǎn)移到的村莊為止。小B希望評測一下這個游戲的難度,因此他需要知道玩家找到所有寶物需要行走的最短路程。但是這個游戲中寶物經(jīng)常變化,有時某個村莊中會突然出現(xiàn)寶物,有時某個村莊內(nèi)的寶物會突然消失,因此小B需要不斷地更新數(shù)據(jù),但是小B太懶了,不愿意自己計算,因此他向你求助。為了簡化問題,我們認為最開始時所有村莊內(nèi)均沒有寶物
題解:
①有一個比較常見的結(jié)論:
從一個點出發(fā),經(jīng)過指定的k個點再回到原點走的路徑的長度是這k個點構(gòu)成的生成樹的邊權(quán)和的兩倍
②這道題如果把有寶藏的點當作特殊點,就相當于維護m棵虛樹.
③每次重新建虛樹是肯定不行的,每次只會修改一個點,插入和刪除這個點一定會伴隨著若干條路徑的改變,找到這些路徑就好了
④畫圖分析可以知道:
這些路徑一定是當前操作的點x和DFS序在它前面的最后一個點L組成的
或者是和DFS序在它后面的第一個點R組成的
⑴如果L存在,那么這條路徑就是L→x
⑵如果R存在路徑R→x到x也會受到影響
⑶如果L和R同時存在,還要排除L→R的影響
⑷ans還要加上tmp是因為直接加dis會使DFS序最小的點x到DFS序最大的點y這條邊x→y只算了一次,要加上另一次
⑤只需要記住每次加點還是刪點都會改變?nèi)舾蓷l路徑對答案的貢獻,找到這些路徑并修改貢獻就好了.
⑥因為要每次要插入一個DFS序,找前驅(qū)后繼,所以用set來維護
?
#include <cstdio> #include <cstring> #include <algorithm> #include <set> using namespace std; typedef long long LL; const int maxn=100000+10; struct Node{int to,next,data; }a[maxn<<1]; int n,m,head[maxn],cnt; int prt[maxn],deep[maxn],size[maxn],son[maxn]; LL dis[maxn]; int Dfn[maxn],Time,top[maxn],pos[maxn]; bool flag[maxn]; LL ans=0; set<int>S; void Init(); void Insert(int,int,int); void DFS1(int,int,int); void DFS2(int,int); int LCA(int,int); LL Dis(int,int); void Solve_Insert(int); void Solve_Delte(int); int main(){// freopen("in.cpp","r",stdin); Init();return 0; } void Init(){scanf("%d%d",&n,&m);int x,y,z;for(int i=1;i<n;i++){scanf("%d%d%d",&x,&y,&z);Insert(x,y,z);Insert(y,x,z);}DFS1(1,0,1);DFS2(1,1);S.insert(0);S.insert(n+1);while(m--){scanf("%d",&x);flag[x]=!flag[x];if(flag[x])Solve_Insert(x);else Solve_Delte(x);} } void Solve_Insert(int x){int L,R,prt1,prt2;LL tmp=0;S.insert(Dfn[x]);L=*--S.find(Dfn[x]);R=*++S.find(Dfn[x]);if(L>=1)ans+=Dis(pos[L],x);if(R<=n)ans+=Dis(pos[R],x);if(L>=1 && R<=n)ans-=Dis(pos[L],pos[R]);L=*++S.find(0),R=*--S.find(n+1);if(L>=1 && R<=n)tmp=Dis(pos[L],pos[R]);printf("%lld\n",tmp+ans); } void Solve_Delte(int x){int L,R,prt1,prt2;LL tmp=0;L=*--S.find(Dfn[x]);R=*++S.find(Dfn[x]);if(L>=1)ans-=Dis(pos[L],x);if(R<=n)ans-=Dis(pos[R],x);if(L>=1 && R<=n)ans+=Dis(pos[L],pos[R]);S.erase(Dfn[x]);L=*++S.find(0),R=*--S.find(n+1);if(L>=1 && R<=n)tmp=Dis(pos[L],pos[R]);printf("%lld\n",tmp+ans); } void Insert(int x,int y,int z){a[++cnt].to=y;a[cnt].next=head[x];a[cnt].data=z;head[x]=cnt; } #define y a[i].to void DFS1(int x,int prt,int deep){::deep[x]=deep;::prt[x]=prt;::size[x]=1;for(int i=head[x];i;i=a[i].next){if(y==prt)continue;dis[y]=dis[x]+a[i].data;DFS1(y,x,deep+1);size[x]+=size[y];if(size[y]>size[son[x]])son[x]=y;} } void DFS2(int x,int top){::Dfn[x]=++Time;::top[x]=top;::pos[Time]=x;if(son[x])DFS2(son[x],top);for(int i=head[x];i;i=a[i].next){if(y==prt[x] || y==son[x])continue;DFS2(y,y);} } #undef y int LCA(int x,int y){while(top[x]!=top[y]){if(deep[top[x]]<deep[top[y]])swap(x,y);x=prt[top[x]];}if(deep[x]<deep[y])return x;return y; } LL Dis(int x,int y){return dis[x]+dis[y]-2*dis[LCA(x,y)]; }?
轉(zhuǎn)載于:https://www.cnblogs.com/holy-unicorn/p/9510293.html
總結(jié)
以上是生活随笔為你收集整理的HYSBZ 3991 寻宝游戏的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: POI解析docx与doc文档中的难点归
- 下一篇: 用计算机求正有理数算术平方根的步骤,用计