洛谷 P1600 天天爱跑步

https://www.luogu.org/problemnew/show/P1600

(仅做记录)

自己的假方法:

每一次跑从a到b:
设l=lca(a,b)
对于以下产生贡献:

a到l的链上所有的点(x)满足
dep[x]+w[x]==dep[a]

l到b的链上(不含l)所有的点(x)满足
dep[x]-dep[l]+dep[a]-dep[l]==w[x]
即dep[x]-w[x]==2*dep[l]-dep[a]

于是每一个点记两个map<int,int>,其中键值对(p,q)表示

“从该点到根的路径上所有满足 dep[x]+w[x]==p(第一个map) / dep[x]-w[x]==p(第二个map) 的点的答案都要加上q"

每一次跑,就是树上差分,乱搞一下。。。

最后每个点x的答案就是以其为根的子树中所有点的两个map分别合并起来后(两个map里面分别有(p,q1)和(p,q2),则合并后有(p,q1+q2)),

在这两个map里面分别查询dep[x]+w[x]和dep[x]-w[x]得到答案的和

因此可以启发式合并处理一下

把平衡树换成值域线段树,启发式合并换成线段树合并就是一个log了。。。。。

扫描二维码关注公众号,回复: 171550 查看本文章

曾经错误:线段树节点作死不开垃圾回收,空间可能算不太对了,原来开7000000都RE了

  1 #include<cstdio>
  2 #include<algorithm>
  3 using namespace std;
  4 int ll=-300000,rr=600000;
  5 namespace SegT
  6 {
  7 int dat[20000100],lc[20000100],rc[20000100],mem;
  8 int L,x;
  9 #define mid (l+((r-l)>>1))
 10 void _addx(int l,int r,int &num)
 11 {
 12     if(!num)    num=++mem;
 13     if(l==r)    {dat[num]+=x;return;}
 14     if(L<=mid)    _addx(l,mid,lc[num]);
 15     else    _addx(mid+1,r,rc[num]);
 16     dat[num]=dat[lc[num]]+dat[rc[num]];
 17 }
 18 int merge(int a,int b)
 19 {
 20     if(!a||!b)    return a+b;
 21     dat[a]+=dat[b];
 22     lc[a]=merge(lc[a],lc[b]);
 23     rc[a]=merge(rc[a],rc[b]);
 24     //delnode(b)
 25     return a;
 26 }
 27 int _query(int l,int r,int num)
 28 {
 29     if(l==r)    return dat[num];
 30     if(L<=mid)    return _query(l,mid,lc[num]);
 31     else    return _query(mid+1,r,rc[num]);
 32 }
 33 void addx(int pos,int dat,int &num)
 34 {
 35     L=pos;x=dat;_addx(ll,rr,num);
 36 }
 37 int query(int pos,int &num)
 38 {
 39     L=pos;return _query(ll,rr,num);
 40 }
 41 #undef mid
 42 }
 43 using SegT::addx;using SegT::query;using SegT::merge;
 44 struct E
 45 {
 46     int to,nxt;
 47 }e[600100];
 48 int f1[300100],ne;
 49 int w[300100],rt1[300100],rt2[300100];
 50 int n,m;
 51 namespace LCA
 52 {
 53     int anc[300100][22],log2n,dep[300100];
 54     void dfs(int u,int fa)
 55     {
 56         int i,k;
 57         anc[u][0]=fa;
 58         for(i=1;i<=log2n;i++)    anc[u][i]=anc[anc[u][i-1]][i-1];
 59         for(k=f1[u];k;k=e[k].nxt)
 60             if(e[k].to!=fa)
 61             {
 62                 dep[e[k].to]=dep[u]+1;
 63                 dfs(e[k].to,u);
 64             }
 65     }
 66     int lca(int a,int b)
 67     {
 68         if(dep[a]<dep[b])    swap(a,b);
 69         int t=dep[a]-dep[b],i;
 70         for(i=log2n;i>=0;i--)
 71             if((1<<i)<=t)
 72                 t-=(1<<i),a=anc[a][i];
 73         if(a==b)    return a;
 74         for(i=log2n;i>=0;i--)
 75             if(anc[a][i]!=anc[b][i])
 76                 a=anc[a][i],b=anc[b][i];
 77         return anc[a][0];
 78     }
 79 }
 80 using LCA::lca;using LCA::dep;
 81 int ans[300100];
 82 void dfs(int u,int fa)
 83 {
 84     int k;
 85     for(k=f1[u];k;k=e[k].nxt)
 86         if(e[k].to!=fa)
 87         {
 88             dfs(e[k].to,u);
 89             rt1[u]=merge(rt1[u],rt1[e[k].to]);
 90             rt2[u]=merge(rt2[u],rt2[e[k].to]);
 91         }
 92     ans[u]=query(dep[u]+w[u],rt1[u])+query(dep[u]-w[u],rt2[u]);
 93 }
 94 int main()
 95 {
 96     int i,a,b,l;
 97     scanf("%d%d",&n,&m);
 98     for(i=1;i<n;i++)
 99     {
100         scanf("%d%d",&a,&b);
101         e[++ne].to=b;e[ne].nxt=f1[a];f1[a]=ne;
102         e[++ne].to=a;e[ne].nxt=f1[b];f1[b]=ne;
103     }
104     for(i=1;i<=n;i++)    scanf("%d",&w[i]);
105     while((1<<(LCA::log2n+1))<=n)    LCA::log2n++;
106     LCA::dfs(1,0);
107     while(m--)
108     {
109         scanf("%d%d",&a,&b);l=lca(a,b);
110         addx(dep[a],1,rt1[a]);addx(dep[a],-1,rt1[LCA::anc[l][0]]);
111         addx(2*dep[l]-dep[a],1,rt2[b]);addx(2*dep[l]-dep[a],-1,rt2[l]);
112     }
113     dfs(1,0);
114     for(i=1;i<=n;i++)    printf("%d ",ans[i]);
115     return 0;
116 }

别人的做法(大概写一下):

先把每个点x答案换一下形式:"以x为根的子树中有多少个起点/终点满足对应条件"

dfs(x)时,先dfs(所有子节点),然后统计自身答案,然后把自身点满足的性质(比如,是起点,是终点,是某一对起点与终点的lca,是某一对起点与终点的lca的父亲)(可以预处理出来)产生的贡献加进一个全局的贡献数组里面

猜你喜欢

转载自www.cnblogs.com/hehe54321/p/9006324.html