1. 程式人生 > >[NOIP2016]天天愛跑步

[NOIP2016]天天愛跑步

題目大意

給定一個n個節點,n1條邊的樹。有m個玩家,第i個玩家從xi走樹上最短路徑到yi。玩家第0秒在自己的起點上,然後每秒移動一條邊,移動到終點後結束移動。
每個節點上有一個觀察員以及權值wi。如果有一個玩家在其移動的第wi恰好到達這個點,那麼這個點上的觀察員就會觀察到他(如果這個點是終點,且玩家在wi秒之前到達不算)。
求每個點上的觀察員分別觀察到了多少個玩家。

1n299998,1m299998

題目分析

我們將每條路徑拆成從出發點到lca,和從lca的下一個點到結束點兩段。
對於第一段,如果在點i時被觀察到,我們可以寫出deep(x)deep(i)=

wi。因此我們對所有deep(x)開權值線段樹(下標為DFN(x),動態開點),在每個玩家路徑lca處掛一個在deep(x)的權值線段樹中DFN(x)位置權值+1的標記。然後每訪問一個點就要先處理該點的所有標記,在deep(i)+wi的權值線段樹裡面查詢子樹的權值和。但是因為在lca上面的點就不能觀察到這個點,因此退出一個點時要撤銷上面的標記。
對於第二段,如果點i時被觀察到了,我們可以寫出deep(x)+deep(i)2deep(lca(x,i))=wi,和上面類似,那麼我們對所有路徑的deep(x)2deep(lca(x,y))開權值線段樹(依然下標為DFN(x),動態開點)。然後將標記掛在l
ca
y的兒子那裡(為了避免算重)。然後在wideep(i)的權值線段樹中查詢。
這樣就要實現一個倍增和常數比較大的權值線段樹, 時間複雜度O(nlog2n)
聽說CCF老爺機要卡log演算法的常,那我也沒辦法了。
update:將我的權值線段樹改成桶就可以O(n)了QwQ。

程式碼實現

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cmath>

using namespace std
; int read() { int x=0,f=1; char ch=getchar(); while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar(); while (isdigit(ch)) x=x*10+ch-'0',ch=getchar(); return x*f; } int buf[30]; void write(int x) { if (x<0) putchar('-'),x=-x; for (;x;x/=10) buf[++buf[0]]=x%10; if (!buf[0]) buf[++buf[0]]=0; for (;buf[0];buf[0]--) putchar('0'+buf[buf[0]]); } const int N=300050; const int LGN=19; const int M=300050; const int E=N<<1; const int Q=M<<1; struct query { bool tp; int key,x; }qy[Q]; int qnxt[Q]; struct D { int key,x,y,lab; }srt[M]; int d; bool operator<(D a,D b){return a.key<b.key;} struct segment_tree { int sum[N*20],son[N*20][2],root[M]; int tot; int newnode() { sum[++tot]=0; son[tot][0]=son[tot][1]=0; return tot; } void update(int x){sum[x]=sum[son[x][0]]+sum[son[x][1]];} void modify(int &x,int y,int l,int r,int delta) { if (!x) x=newnode(); if (l==r) { sum[x]+=delta; return; } int mid=l+r>>1; if (y<=mid) modify(son[x][0],y,l,mid,delta); else modify(son[x][1],y,mid+1,r,delta); update(x); } int query(int x,int st,int en,int l,int r) { if (!x) return 0; if (st==l&&en==r) return sum[x]; int mid=l+r>>1; if (en<=mid) return query(son[x][0],st,en,l,mid); else if (mid+1<=st) return query(son[x][1],st,en,mid+1,r); else return query(son[x][0],st,mid,l,mid)+query(son[x][1],mid+1,en,mid+1,r); } }t[2]; int deep[N],last[N],size[N],DFN[N],qlst[N],w[N],ans[N]; int tov[E],nxt[E]; int fa[N][LGN]; int n,m,tot,idx,lgn,cnt,dif; void insert(int x,int y){tov[++tot]=y,nxt[tot]=last[x],last[x]=tot;} void hang(int x,int y){qnxt[y]=qlst[x],qlst[x]=y;} void dfs(int x) { DFN[x]=++idx,size[x]=1; for (int i=last[x],y;i;i=nxt[i]) if ((y=tov[i])!=fa[x][0]) fa[y][0]=x,deep[y]=deep[x]+1,dfs(y),size[x]+=size[y]; } void pre() { lgn=trunc(log(n)/log(2)); for (int j=1;j<=lgn;j++) for (int i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1]; } int adjust(int x,int d) { for (int i=lgn;i>=0;i--) if (deep[fa[x][i]]>=d) x=fa[x][i]; return x; } int lca(int x,int y) { if (deep[x]>deep[y]) swap(x,y); y=adjust(y,deep[x]); if (x==y) return x; for (int i=lgn;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } void proc() { sort(srt+1,srt+1+d); dif=0,srt[0].key=-n*2; for (int i=1;i<=d;i++) { dif+=srt[i].key!=srt[i-1].key; srt[i].lab=dif; qy[++cnt].tp=1,qy[cnt].key=dif,qy[cnt].x=srt[i].y; hang(srt[i].x,cnt); } } query tmp; int search(int aim) { int ret=0; int l=1,r=d,mid; while (l<=r) { mid=l+r>>1; if (srt[mid].key<=aim) ret=mid,l=mid+1; else r=mid-1; } if (srt[ret].key==aim) return srt[ret].lab; return 0; } //int total=0; void calc(int x) { for (int i=qlst[x];i;i=qnxt[i]) { tmp=qy[i]; //printf("0 %d %d\n",x,i); t[tmp.tp].modify(t[tmp.tp].root[tmp.key],DFN[tmp.x],1,n,1); } for (int i=last[x],y;i;i=nxt[i]) if ((y=tov[i])!=fa[x][0]) calc(y); ans[x]=0; if (w[x]+deep[x]<=n) ans[x]+=t[0].query(t[0].root[w[x]+deep[x]],DFN[x],DFN[x]+size[x]-1,1,n); int f=search(deep[x]-w[x]); if (f) ans[x]+=t[1].query(t[1].root[f],DFN[x],DFN[x]+size[x]-1,1,n); for (int i=qlst[x];i;i=qnxt[i]) { tmp=qy[i]; /*if (x==3&&i==555634) { tmp.tp=qy[i].tp; }*/ //printf("1 %d %d\n",x,i); t[tmp.tp].modify(t[tmp.tp].root[tmp.key],DFN[tmp.x],1,n,-1); //printf("%d\n",++total); } } int main() { freopen("running.in","r",stdin),freopen("running.out","w",stdout); n=read(),m=read(); for (int i=1,x,y;i<n;i++) { x=read(),y=read(); insert(x,y),insert(y,x); } fa[1][0]=0,deep[1]=1,dfs(1),pre(); for (int i=1;i<=n;i++) w[i]=read(); for (int x,y,z;m--;) { x=read(),y=read(),z=lca(x,y); qy[++cnt].tp=0,qy[cnt].key=deep[x],qy[cnt].x=x; hang(z,cnt); if (!(DFN[y]<=DFN[x]&&DFN[x]<=DFN[y]+size[y]-1)) srt[++d].key=deep[z]*2-deep[x],srt[d].x=adjust(y,deep[z]+1),srt[d].y=y; } proc(),calc(1); for (int i=1;i<n;i++) write(ans[i]),putchar(' '); write(ans[n]),putchar('\n'); fclose(stdin),fclose(stdout); return 0; }