1. 程式人生 > >【bzoj3924&&luogu3345】幻想鄉戰略遊戲

【bzoj3924&&luogu3345】幻想鄉戰略遊戲

roo 當前 def 目前 style %d col ext continue

這題可以用線段樹做,不過正解恐怕是動態點分治?(點分樹)

簡單介紹下動態點分治的概念:在點分治的過程中,一般我們面對的問題都是靜態的。如果涉及到修改這類的操作,我們就希望找到我們是如何處理到當前的修改點的,換而言之,我們希望記錄下點分治的過程,這樣可以通過爬點分樹等操作消除影響。

對於每個節點我們保存這個子樹的dv的總和已經把該節點作為點的答案值

這樣對於修改能在$O(logn)的時間內解決

簡要看下題意:詢問樹的帶權重心,加修改。

如果不加修改,找到樹的重心就是一個樹形dp的傻題。

尋找答案的時候,我們可以發現,如果現在節點的子樹dv和*2大於總節點,那麽向那個方向過去一定比原方案好。

考慮如何處理修改?

我們先從根節點開始,若發現答案在某棵子樹時,我們考慮如何使其兒子節點的答案轉變為整個樹的答案,可以發現把除這個子樹外的所有節點可以縮成一個節點並連在這棵子樹上,然後就可以一直這樣做下去,找到操作之後再把這些撤銷。

下面是鹹魚給出的一些實現細節:

1.跟虛樹一樣(不知道的可以百度“世界樹”),這也是要在第二棵樹上對原樹進行統計,所以一定千萬小心不要弄混,我學到的辦法是對原樹進行封裝,這樣即使不小心訪問錯了他也會給你提示。

2.對樹進行歐拉序遍歷可以利用rmq在$O(1)$查詢LCA,這點也是特別好懂的。(只會樹剖的蒟蒻瑟瑟發抖)

代碼總共113行,應該是可讀性比較好的。

zyz的那個碼風清奇根本不能看啊QAQ

應該是目前比較能看的點分樹代碼之一吧。

#include<bits/stdc++.h>
#define N 100010
typedef long long ll;
using namespace std;
int n,m,x,y,z,size[N],tot;
int head[N],sum,rt,f[N],vis[N],par[N],cnt,lg[N<<2];
ll dis1[N],dis2[N],sumv[N];
struct Edge{int u,v,next,w;}G[2*N];
void Addedge(int u,int v,int w){
    G[++tot].u=u;G[tot].v=v;G[tot].w=w;G[tot].next=head[u];head[u]=tot;
}
struct Orinal_Tree{ int head[N],cnt,tot; int st[N<<2][21],dis[N],tpos[N<<1]; struct edge{int u,v,next,w;}E[2*N]; inline void addedge(int u,int v,int w){ E[++tot].u=u;E[tot].v=v;E[tot].w=w;E[tot].next=head[u];head[u]=tot; E[++tot].u=v;E[tot].v=u;E[tot].w=w;E[tot].next=head[v];head[v]=tot; } inline int getdis(int u,int v){ if(tpos[u]>tpos[v])swap(u,v); int k=lg[tpos[v]-tpos[u]+1]; return dis[u]+dis[v]-2*min(st[tpos[u]][k],st[tpos[v]-(1<<k)+1][k]); } void dfs(int u,int fa){ st[++cnt][0]=dis[u];tpos[u]=cnt; for(int i=head[u];i;i=E[i].next){ int v=E[i].v;if(v==fa)continue; dis[v]=dis[u]+E[i].w; dfs(v,u); st[++cnt][0]=dis[u]; } } inline void initrmq(){ memset(tpos,0,sizeof(tpos));cnt=0;tot=0;dis[1]=0; dfs(1,0); for(int j=1;(1<<j)<=cnt;j++) for(int i=1;i+(1<<j)-1<=cnt&&i<=cnt;i++) st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); } }T; void getroot(int u,int fa){ size[u]=1;f[u]=0; for(int i=T.head[u];i;i=T.E[i].next){ int v=T.E[i].v;if(vis[v]||v==fa)continue; getroot(v,u);size[u]+=size[v]; f[u]=max(f[u],size[v]); } f[u]=max(f[u],sum-size[u]); if(f[u]<f[rt])rt=u; } void work(int u,int fa){ vis[u]=1;par[u]=fa;//printf("%d\n",u);puts("!!!"); for(int i=T.head[u];i;i=T.E[i].next){ int v=T.E[i].v;if(vis[v])continue; sum=size[v];f[0]=size[v];rt=0; getroot(v,0);Addedge(u,rt,v); work(rt,u); } } inline void ins(int u,int val){ sumv[u]+=val; for(int i=u;par[i];i=par[i]){ int dist=T.getdis(par[i],u); dis1[par[i]]+=(ll)dist*val; dis2[i]+=(ll)dist*val; sumv[par[i]]+=val; } } inline ll calc(int u){ ll ans=dis1[u]; for(int i=u;par[i];i=par[i]){ int dist=T.getdis(par[i],u); ans+=dis1[par[i]]-dis2[i]; ans+=dist*(sumv[par[i]]-sumv[i]); } return ans; } ll query(int u){ ll ans=calc(u);//printf("%lld\n",ans); for(int i=head[u];i;i=G[i].next){ ll tmp=calc(G[i].w); if(tmp<ans)return query(G[i].v); } return ans; } inline int read(){ int f=1,x=0;char ch; do{ch=getchar();if(ch==-)f=-1;}while(ch<0||ch>9); do{x=x*10+ch-0;ch=getchar();}while(ch>=0&&ch<=9); return f*x; } void init(){ memset(dis1,0,sizeof(dis1)); memset(dis2,0,sizeof(dis2)); memset(sumv,0,sizeof(sumv)); lg[0]=-1; for (int i=1;i<(N<<2);i++)lg[i]=lg[i>>1]+1; n=read();m=read();//printf("%d %d\n",n,m); for (int i=1;i<n;i++) x=read(),y=read(),z=read(),T.addedge(x,y,z); } int main(){ init();T.initrmq();sum=n;f[0]=n; rt=0;getroot(1,0); int LastOrder=rt;work(rt,0);rt=LastOrder; //for(int i=1;i<=20;i++)//printf("%d ",T.head[i]);puts(""); for(int i=1;i<=m;i++){ x=read();y=read();ins(x,y); printf("%lld\n",query(rt)); } return 0; }

【bzoj3924&&luogu3345】幻想鄉戰略遊戲