HDU5957 Query on a graph
阿新 • • 發佈:2018-12-13
線段樹 BFS序
**題目大意:**一個基環樹,有點權。有詢問和修改操作。修改有三個引數,表示把距離不超過(包括自己,下同)的點都加上。查詢有兩個引數,表示求距離不超過的點權和。。
先別管那條非樹邊。當的時候是維護它爸爸和它兒子,當的時候還要維護它爺爺、它兄弟和它孫子。
我們對這棵樹求一遍BFS序,這樣當時要維護的就是兩個點和一個區間,當時就是兩個點和三個區間。線段樹維護即可。對非樹邊的影響特殊處理。
具體實現比較繁瑣,詳見程式碼:
#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