1. 程式人生 > >bzoj 3730 震波——動態點分治+樹狀陣列

bzoj 3730 震波——動態點分治+樹狀陣列

題目:https://www.lydsy.com/JudgeOnline/problem.php?id=3730

查詢一個點可以轉化為查詢點分樹上自己到根的路徑上每個點對應範圍答案。可用樹狀陣列 f 。

但有重複,所以再開一個樹狀陣列 g 記錄上一層重心的含自己的那棵子樹裡各種距離的點值和。

查詢的時候如果上一層的重心有貢獻,就加上它的 f 的對應範圍,再減去這一層的 g 的對應範圍。這兩個範圍是一樣的,因為這一層的 g 也是相對上一層重心的距離。

修改的時候列舉每層,改一下這一層的 f 和下一層的 g 。只是這樣寫比較方便而已。

可以用 rmq O(1) 求兩點距離,就是尤拉序裡兩點之間深度最小的就是 LCA 的深度,所以用 ST 表查詢之類的。

所有的樹狀陣列一共應該是 nlogn 個點,所以自己開了 nlogn 的陣列,給每個點一個 l 和 r 表示它們用的是哪一段。然後瘋狂 TLE 。可能也是 query 和 mdfy 的地方寫得不好。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define il inline
#define rg register
using namespace std;
const int N=1e5+5,K=17;
int n,w[N],hd[N],xnt,to[N<<1
],nxt[N<<1],ans; int tim,tot,dep[N],q[N<<1],ps[N],st[N][K+1],lg[N<<1],bin[K+5]; int mn,rt,siz[N],pre[N],f[N*K],g[N*K<<1],fl[N],fr[N],gl[N],gr[N],ftp,gtp; bool vis[N]; il int rdn() { rg int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } int dg[20]; il void wrt(int x) { if(!x){puts("0");return;} rg int tt=0;while(x)dg[++tt]=x%10,x/=10; for(;tt;tt--)putchar(dg[tt]+'0');puts(""); } il int Mx(rg int a,rg int b){return a>b?a:b;} il int Mn(rg int a,rg int b){return a<b?a:b;} il void add(rg int x,rg int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;} il void init_dfs(rg int cr,rg int fa) { dep[cr]=dep[fa]+1;q[++tot]=cr;ps[cr]=tot; for(rg int i=hd[cr],v;i;i=nxt[i]) if((v=to[i])!=fa)init_dfs(v,cr),q[++tot]=cr;// } il void init_dis() { init_dfs(1,0); lg[1]=0;for(rg int i=2;i<=tot;i++)lg[i]=lg[i>>1]+1;//lg[1]=0! bin[0]=1;for(rg int i=1;i<=lg[tot];i++)bin[i]=bin[i-1]<<1; for(rg int i=1;i<=tot;i++)st[i][0]=dep[q[i]]; for(rg int t=1;t<=lg[tot];t++) for(rg int i=1;i+bin[t]-1<=tot;i++) st[i][t]=Mn(st[i][t-1],st[i+bin[t-1]][t-1]);//bin[t-1]!! } il int pd(rg int x,rg int y) { rg int u=ps[x],v=ps[y];if(u>v)swap(u,v); rg int t=lg[v-u+1],d=Mn(st[u][t],st[v-bin[t]+1][t]); return dep[x]-d+dep[y]-d; } il void add(rg int x,rg int l,rg int r,rg int k){for(rg int d=r-l;x<=d;x+=(x&-x))f[x+l]+=k;} il void addx(rg int x,rg int l,rg int r,rg int k){for(rg int d=r-l;x<=d;x+=(x&-x))g[x+l]+=k;} il int qry(rg int x,rg int l,rg int r){if(!x)return 0;rg int ret=0;for(;x;x-=(x&-x))ret+=f[x+l];return ret;} il int qryx(rg int x,rg int l,rg int r){rg int ret=0;for(;x;x-=(x&-x))ret+=g[x+l];return ret;} il void getrt(rg int cr,rg int fa,rg int s) { siz[cr]=1;rg int mx=0; for(rg int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=fa) { getrt(v,cr,s);siz[cr]+=siz[v];mx=Mx(mx,siz[v]); } mx=Mx(mx,s-siz[cr]); if(mx<mn)mn=mx,rt=cr; } il void dfs(rg int cr,rg int fa,rg int dis) { if(dis)add(dis,fl[rt],fr[rt],w[cr]); for(rg int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=fa)dfs(v,cr,dis+1); } il void dfsx(rg int cr,rg int fa,rg int dis) { addx(dis,gl[rt],gr[rt],w[cr]); // printf("adx:rt=%d cr=%d dis=%d(w=%d)\n",rt,cr,dis,w[cr]); for(rg int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=fa)dfsx(v,cr,dis+1); } il void init(rg int cr,rg int lst,rg int pr,rg int s) { /*vis[cr]=1;*/pre[cr]=pr; fl[cr]=ftp;ftp+=mn;fr[cr]=ftp;//l:1,r:mn gl[cr]=gtp;gtp+=(mn<<1)+1;gr[cr]=gtp;//+1!!i.e.:only one point dfs(cr,0,0);if(lst)dfsx(lst,0,1); vis[cr]=1;//for dfsx may cross rt for(rg int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]]) { rg int ts=(siz[v]<siz[cr]?siz[v]:s-siz[cr]); mn=N;getrt(v,cr,ts);init(rt,v,cr,ts); } } il void query(rg int cr,rg int x,rg int dis,rg int yd) { for(;pre[cr];cr=pre[cr]) { ans+=qry(Mn(dis,fr[cr]-fl[cr]),fl[cr],fr[cr])+w[cr]; dis=yd-pd(x,pre[cr]); if(dis<0)return;// if(dis)ans-=qryx(Mn(dis,gr[cr]-gl[cr]),gl[cr],gr[cr]); } ans+=qry(Mn(dis,fr[cr]-fl[cr]),fl[cr],fr[cr])+w[cr]; } il void mdfy(rg int cr,rg int k,rg int x,rg int dis) { for(;pre[cr];cr=pre[cr]) { if(dis)add(dis,fl[cr],fr[cr],k); dis=pd(x,pre[cr]); addx(dis,gl[cr],gr[cr],k); } if(dis)add(dis,fl[cr],fr[cr],k); } int main() { n=rdn(); rg int Q=rdn(); for(rg int i=1;i<=n;i++)w[i]=rdn(); for(rg int i=1,u,v;i<n;i++)u=rdn(),v=rdn(),add(u,v),add(v,u); init_dis(); mn=N;getrt(1,0,n);init(rt,0,0,n); rg int op,x,k; while(Q--) { op=rdn();x=rdn()^ans;k=rdn()^ans;if(!op)ans=0;//if() if(!op) query(x,x,k,k),wrt(ans);//,printf("%d\n",ans); else mdfy(x,k-w[x],x,0),w[x]=k; } return 0; }
View Code

看了半天題解,寫成線段樹版本。還是 TTT 。

如果給這份程式碼寫上很多 inline 和 register ,就會在 bzoj 上 CE 。會報錯 l+r>>1 位置沒加括號,然而加上之後會沒有錯誤資訊地 CE 。也不知是怎麼回事。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ls Ls[cr]
#define rs Rs[cr]
using namespace std;
const int N=1e5+5,M=N*200,K=17;
int n,w[N],hd[N],xnt,to[N<<1],nxt[N<<1],ans;
int tot,dep[N],ps[N],st[N<<1][K+1],lg[N<<1],bin[K+5];//st[N<<1]
int mn,rot,siz[N],pre[N],rt[N<<1],sm[M],Ls[M],Rs[M];
bool vis[N];
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
  while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
  return fx?ret:-ret;
}
int dg[20];
void wrt(int x)
{
  if(!x){puts("0");return;}
  int tt=0;while(x)dg[++tt]=x%10,x/=10;
  for(;tt;tt--)putchar(dg[tt]+'0');puts("");
}
int Mx(int a,int b){return a>b?a:b;}
int Mn(int a,int b){return a<b?a:b;}
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
void init_dfs(int cr,int fa)
{
  dep[cr]=dep[fa]+1;ps[cr]=++tot;st[tot][0]=dep[cr];
  for(int i=hd[cr],v;i;i=nxt[i])
    if((v=to[i])!=fa)init_dfs(v,cr),st[++tot][0]=dep[cr];//
}
void init_dis()
{
  init_dfs(1,0);
  lg[1]=0;for(int i=2;i<=tot;i++)lg[i]=lg[i>>1]+1;//lg[1]=0!
  bin[0]=1;for(int i=1;i<=lg[tot];i++)bin[i]=bin[i-1]<<1;
  for(int t=1;t<=lg[tot];t++)
    for(int i=1;i+bin[t]-1<=tot;i++)
      st[i][t]=Mn(st[i][t-1],st[i+bin[t-1]][t-1]);//bin[t-1]!!
}
int pd(int x,int y)
{
  int u=ps[x],v=ps[y];if(u>v)swap(u,v);
  int t=lg[v-u+1],d=Mn(st[u][t],st[v-bin[t]+1][t]);
  return dep[x]-d+dep[y]-d;
}
void getrt(int cr,int fa,int s)
{
  siz[cr]=1;int mx=0;
  for(int i=hd[cr],v;i;i=nxt[i])
    if(!vis[v=to[i]]&&v!=fa)
      {
    getrt(v,cr,s);siz[cr]+=siz[v];mx=Mx(mx,siz[v]);
      }
  mx=Mx(mx,s-siz[cr]); if(mx<mn)mn=mx,rot=cr;
}
void init(int cr,int s)
{
  vis[cr]=1;
  for(int i=hd[cr],v;i;i=nxt[i])
    if(!vis[v=to[i]])
      {
    int ts=(siz[v]<siz[cr]?siz[v]:s-siz[cr]);
    mn=N;getrt(v,cr,ts);pre[rot]=cr;init(rot,ts);
      }
}
int qry(int l,int r,int cr,int R)
{
  if(!cr)return 0;
  if(r<=R)return sm[cr]; int mid=l+r>>1;
  if(R>mid)return sm[ls]+qry(mid+1,r,rs,R);
  return qry(l,mid,ls,R);
}
void mdfy(int l,int r,int &cr,int p,int k)
{
  if(!cr)cr=++tot; sm[cr]+=k;
  if(l==r)return;  int mid=l+r>>1;
  if(p<=mid)mdfy(l,mid,ls,p,k);
  else mdfy(mid+1,r,rs,p,k);
}
void query(int cr,int yd)
{
  int x=cr,dis=yd; ans+=qry(0,n,rt[cr],dis);
  for(;pre[cr];cr=pre[cr])
    {
      dis=yd-pd(x,pre[cr]); if(dis<0)continue;
      ans+=qry(0,n,rt[pre[cr]],dis);
      ans-=qry(0,n,rt[cr+n],dis);
    }
}
void mdfy(int cr,int k)
{
  int x=cr,dis; mdfy(0,n,rt[cr],0,k);
  for(;pre[cr];cr=pre[cr])
    {
      dis=pd(x,pre[cr]);
      mdfy(0,n,rt[pre[cr]],dis,k);
      mdfy(0,n,rt[cr+n],dis,k);
    }
}
int main()
{
  n=rdn(); int Q=rdn();
  for(int i=1;i<=n;i++)w[i]=rdn();
  for(int i=1,u,v;i<n;i++)u=rdn(),v=rdn(),add(u,v),add(v,u);
  init_dis();  tot=0;
  mn=N;getrt(1,0,n);init(rot,n);
  for(int i=1;i<=n;i++)mdfy(i,w[i]);
  int op,x,k;
  while(Q--)
    {
      op=rdn();x=rdn()^ans;k=rdn()^ans;if(!op)ans=0;//if()
      if(!op) query(x,k),wrt(ans);
      else mdfy(x,k-w[x]),w[x]=k;
    }
  return 0;
}
View Code

最後寫了 vector 實現的樹狀陣列。 v . resize( k ) 可以給 v 開出 k 大小,角標是 0 到 k-1 。

人家還存下來了每個點的 log 個點分樹上祖先以及自己到它們的距離,感覺好清爽。(速度也變快了?)

樹狀陣列查不到 0 位置的值,所以那裡手動加上 w[cr] (就和自己第一個版本一樣)。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1e5+5,K=20;
int n,w[N],hd[N],xnt,to[N<<1],nxt[N<<1],ans,mn,rt,siz[N];
int dep[N],pre[N][K],fsz[N],gsz[N],dis[N][K]; vector<int> f[N],g[N];
bool vis[N];
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
  while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
  return fx?ret:-ret;
}
int dg[20];
void wrt(int x)
{
  if(!x){puts("0");return;}
  int tt=0;while(x)dg[++tt]=x%10,x/=10;
  for(;tt;tt--)putchar(dg[tt]+'0');puts("");
}
int Mx(int a,int b){return a>b?a:b;}
int Mn(int a,int b){return a<b?a:b;}
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
void add(int cr,int x,int k){for(x=Mn(x,fsz[cr]);x&&x<=fsz[cr];x+=(x&-x))f[cr][x]+=k;}//x&&...
int qry(int cr,int x){int ret=0;for(x=Mn(x,fsz[cr]);x;x-=(x&-x))ret+=f[cr][x];return ret+w[cr];}
void addx(int cr,int x,int k){for(x=Mn(x,gsz[cr]);x&&x<=gsz[cr];x+=(x&-x))g[cr][x]+=k;}
int qryx(int cr,int x){int ret=0;for(x=Mn(x,gsz[cr]);x;x-=(x&-x))ret+=g[cr][x];return ret;}
void getrt(int cr,int fa,int s)
{
  siz[cr]=1;int mx=0;
  for(int i=hd[cr],v;i;i=nxt[i])
    if(!vis[v=to[i]]&&v!=fa)
      {
    getrt(v,cr,s);siz[cr]+=siz[v];mx=Mx(mx,siz[v]);
      }
  mx=Mx(mx,s-siz[cr]); if(mx<mn)mn=mx,rt=cr;
}
void dfs(int cr,int fa,int ds)
{
  pre[cr][++dep[cr]]=rt;dis[cr][dep[cr]]=ds;
  for(int i=hd[cr],v;i;i=nxt[i])
    if(!vis[v=to[i]]&&v!=fa)dfs(v,cr,ds+1);
}
void init(int cr,int s)
{
  fsz[cr]=mn;gsz[cr]=(mn<<1)+1;
  f[cr].resize(fsz[cr]+1);g[cr].resize(gsz[cr]+1);
  vis[cr]=1;dfs(cr,0,0);
  for(int i=hd[cr],v;i;i=nxt[i])
    if(!vis[v=to[i]])
      {
    int ts=(siz[v]<siz[cr]?siz[v]:s-siz[cr]);
    mn=N;getrt(v,cr,ts);init(rt,ts);
      }
}
void query(int cr,int yd)
{
  ans+=qry(cr,yd);
  for(int i=dep[cr]-1;i;i--)
    {
      if(dis[cr][i]>yd)continue;
      ans+=qry(pre[cr][i],yd-dis[cr][i]);
      ans-=qryx(pre[cr][i+1],yd-dis[cr][i]);
    }
}
void mdfy(int cr,int k)
{
  for(int i=dep[cr]-1;i;i--)
    {
      add(pre[cr][i],dis[cr][i],k);
      addx(pre[cr][i+1],dis[cr][i],k);
    }
}
int main()
{
  n=rdn(); int Q=rdn();
  for(int i=1;i<=n;i++)w[i]=rdn();
  for(int i=1,u,v;i<n;i++)u=rdn(),v=rdn(),add(u,v),add(v,u);
  mn=N;getrt(1,0,n);init(rt,n);
  for(int i=1;i<=n;i++)mdfy(i,w[i]);
  int op,x,k;
  while(Q--)
    {
      op=rdn();x=rdn()^ans;k=rdn()^ans;if(!op)ans=0;//if()
      if(!op) query(x,k),wrt(ans);
      else mdfy(x,k-w[x]),w[x]=k;
    }
  return 0;
}