1. 程式人生 > >HDU5957 Query on a graph

HDU5957 Query on a graph

線段樹 BFS序

題目傳送門

**題目大意:**一個基環樹,有點權。有詢問和修改操作。修改有三個引數u,k,du,k,d,表示把距離uu不超過kk(包括自己,下同)的點都加上dd。查詢有兩個引數u,ku,k,表示求距離uu不超過kk的點權和。n1e5,k2n\leq 1e5,k\leq 2

先別管那條非樹邊。當k=1k=1的時候是維護它爸爸和它兒子,當k=2k=2的時候還要維護它爺爺、它兄弟和它孫子。

我們對這棵樹求一遍BFS序,這樣當k=1k=1時要維護的就是兩個點和一個區間,當k=2k=2時就是兩個點和三個區間。線段樹維護即可。對非樹邊的影響特殊處理。

具體實現比較繁瑣,詳見程式碼:

#include<queue>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define F inline
using namespace std;
typedef long long LL;
struct edge{ int nxt,to; }ed[N<<1];
struct tree{ int l,r; LL x,f; }t[N<<2];
int n,
m,k,ti,v1,v2,h[N],fa[N],in[N],l[N][3],r[N][3]; queue <int> q; bool f[N],f1[N],f2[N]; //l/r[x][0]存它兒子的區間,[x][1]存它孫子的區間,[x][2]存它兄弟的區間 //f1/f2表示到它距離為1的點中有沒有非樹邊連的兩個點 F char readc(){ static char buf[100000],*l=buf,*r=buf; if (l==r) r=(l=buf)+fread(buf,1,100000,stdin); return l==r?EOF:*l++; } F int _read(){ int x=
0,f=1; char ch=readc(); while (!isdigit(ch)&&ch!='M'&&ch!='Q'){ if (ch=='-') f=-1; ch=readc(); } if (ch=='M'||ch=='Q') return ch; while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc(); return x*f; } F void bfs(){ for (int i=0;i<=n;i++) l[i][0]=1e9; fa[1]=0,f[1]=true,q.push(1); while (!q.empty()){ int x=q.front(); in[x]=++ti,q.pop(); r[fa[x]][0]=max(r[fa[x]][0],ti); l[fa[x]][0]=min(l[fa[x]][0],ti); for (int i=h[x],v;i;i=ed[i].nxt) if ((v=ed[i].to)!=fa[x]&&!f[v]) fa[v]=x,f[v]=true,q.push(v); } } #define add(x,y) ed[++k]=(edge){h[x],y},h[x]=k int findfa(int x){ return x==fa[x]?x:fa[x]=findfa(fa[x]); } F void pshp(int x){ t[x].x=t[x<<1].x+t[x<<1|1].x; } F void add1(int x,LL w){ t[x].x+=w*(t[x].r-t[x].l+1),t[x].f+=w; } F void pshd(int x){ if (t[x].f) add1(x<<1,t[x].f),add1(x<<1|1,t[x].f),t[x].f=0; } void build(int x,int l,int r){ t[x]=(tree){l,r,0,0}; if (l==r) return; int mid=l+r>>1; build(x<<1,l,mid),build(x<<1|1,mid+1,r); } void mdfy(int x,int l,int r,LL w){ if (t[x].l>r||t[x].r<l) return; if (t[x].l>=l&&t[x].r<=r) return add1(x,w); pshd(x),mdfy(x<<1,l,r,w),mdfy(x<<1|1,l,r,w),pshp(x); } LL srch(int x,int l,int r){ if (t[x].l>r||t[x].r<l) return 0; if (t[x].l>=l&&t[x].r<=r) return t[x].x; return pshd(x),srch(x<<1,l,r)+srch(x<<1|1,l,r); } F bool pd(int y,int x){ if (fa[x]==y||fa[fa[x]]==y) return false; for (int i=0;i<3;i++) if (in[y]>=l[x][i]&&in[y]<=r[x][i]) return false; return true; } F void nsrt(){//分類討論修改範圍,下同 int x=_read(),k=_read(),d=_read(); bool ff1=true,ff2=true; if (k==0) mdfy(1,in[x],in[x],d); if (k==1){ mdfy(1,in[x],in[x],d),mdfy(1,l[x][0],r[x][0],d); if (fa[x]) mdfy(1,in[fa[x]],in[fa[x]],d); if (x==v1) mdfy(1,in[v2],in[v2],d); if (x==v2) mdfy(1,in[v1],in[v1],d); } if (k==2){ for (int i=0;i<3;i++) mdfy(1,l[x][i],r[x][i],d); if (x==v1){//對非樹邊連的兩個點特判 if (pd(v2,x)) mdfy(1,in[v2],in[v2],d); if (pd(fa[v2],x)) mdfy(1,in[fa[v2]],in[fa[v2]],d); mdfy(1,l[v2][0],r[v2][0],d),ff1=fa[fa[x]]!=v2,ff2=fa[fa[fa[x]]]!=v2; } if (x==v2){ if (pd(v1,x)) mdfy(1,in[v1],in[v1],d); if (pd(fa[v1],x)) mdfy(1,in[fa[v1]],in[fa[v1]],d); mdfy(1,l[v1][0],r[v1][0],d),ff1=fa[fa[x]]!=v1,ff2=fa[fa[fa[x]]]!=v1; //要修改到另一個點的距離為1的點,注意不要重複 } if (fa[x]&&ff1) mdfy(1,in[fa[x]],in[fa[x]],d); if (fa[fa[x]]&&ff2) mdfy(1,in[fa[fa[x]]],in[fa[fa[x]]],d); if (f1[x]&&pd(v2,x)) mdfy(1,in[v2],in[v2],d); if (f2[x]&&pd(v1,x)) mdfy(1,in[v1],in[v1],d); } } F void find(){ int x=_read(),k=_read(); LL ans=0; bool ff1=true,ff2=true; if (k==0) ans+=srch(1,in[x],in[x]); if (k==1){ ans+=srch(1,in[x],in[x])+srch(1,l[x][0],r[x][0]); if (fa[x]) ans+=srch(1,in[fa[x]],in[fa[x]]); if (x==v1) ans+=srch(1,in[v2],in[v2]); if (x==v2) ans+=srch(1,in[v1],in[v1]); } if (k==2){ for (int i=0;i<3;i++) ans+=srch(1,l[x][i],r[x][i]); if (x==v1){ if (pd(v2,x)) ans+=srch(1,in[v2],in[v2]); if (pd(fa[v2],x)) ans+=srch(1,in[fa[v2]],in[fa[v2]]); ans+=srch(1,l[v2][0],r[v2][0]),ff1=fa[fa[x]]!=v2,ff2=fa[fa[fa[x]]]!=v2; } if (x==v2){ if (pd(v1,x)) ans+=srch(1,in[v1],in[v1]); if (pd(fa[v1