bzoj 3073 [Pa2011]Journeys ——线段树优化连边

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3073

建两棵线段树,一棵孩子向父亲连边,是走出去的;一棵父亲向孩子连边,是走进来的。

注意第二棵线段树的叶子向第一棵线段树的叶子连边。

在树上节点间连边的时候,不是 log^2 地直接连,而要新建一个节点作中转点,这样边数就是 2*log 的;连向中转点和连出去的边一部是0一部是1或者全是0.5即可。

注意连无向边。两种方向当然是两个中转点。

边数 3e7 似乎还是小。但能过。更大就开不下了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=4e6+5,M=3e7+5,K=5e5+5;
int n,m,p,tot,hd[N],xnt,to[M],nxt[M],w[M],ls[K<<2],rs[K<<2],dis[N];
int rt,d0[K],d1[K];
bool vis[N];
priority_queue<pair<int,int> > q;
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<<3)+(ret<<1)+ch-'0',ch=getchar();
  return fx?ret:-ret;
}
void wrt(int x)
{
  if(x<0)putchar('-'),x=-x;
  if(x<10){putchar(x+'0');return;}
  wrt(x/10); putchar(x%10+'0');
}
void add(int x,int y,int z)
{
  to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;w[xnt]=z;
}
void build(int l,int r,int cr,bool fx)
{
  if(l==r)
    {
      if(!fx)d0[l]=cr;
      else d1[l]=cr,add(cr,d0[l],0);
      return;
    }
  int mid=l+r>>1;
  ls[cr]=++tot; build(l,mid,ls[cr],fx);
  rs[cr]=++tot; build(mid+1,r,rs[cr],fx);
  if(fx)add(cr,ls[cr],0),add(cr,rs[cr],0);
  else add(ls[cr],cr,0),add(rs[cr],cr,0);
}
void mdfy(int l,int r,int cr,int L,int R,bool fx)
{
  if(l>=L&&r<=R)
    {
      if(!fx)add(cr,tot,1);
      else add(tot,cr,0);
      return;
    }
  int mid=l+r>>1;
  if(L<=mid)mdfy(l,mid,ls[cr],L,R,fx);
  if(mid<R)mdfy(mid+1,r,rs[cr],L,R,fx);
}
int main()
{
  n=rdn(); m=rdn(); p=rdn();
  tot=1; build(1,n,1,0);
  rt=++tot; build(1,n,rt,1);
  for(int i=1,a,b,c,d;i<=m;i++)
    {
      a=rdn(); b=rdn(); c=rdn(); d=rdn();
      tot++; mdfy(1,n,1,a,b,0); mdfy(1,n,rt,c,d,1);
      tot++; mdfy(1,n,1,c,d,0); mdfy(1,n,rt,a,b,1);
    }
  memset(dis,0x3f,sizeof dis);
  dis[d1[p]]=0; q.push(make_pair(0,d1[p]));
  while(q.size())
    {
      int k=q.top().second; q.pop();
      if(vis[k])continue; vis[k]=1;
      for(int i=hd[k],v;i;i=nxt[i])
    {
      if(dis[v=to[i]]>dis[k]+w[i])
        dis[v]=dis[k]+w[i],q.push(make_pair(-dis[v],v));
    }
    }
  for(int i=1;i<=n;i++,puts(""))wrt(dis[d1[i]]);
  return 0;
}
原文地址:https://www.cnblogs.com/Narh/p/9869651.html