洛谷 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了。。。。。
曾经错误:线段树节点作死不开垃圾回收,空间可能算不太对了,原来开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的父亲)(可以预处理出来)产生的贡献加进一个全局的贡献数组里面