标签:tran 就会 ios upd 介绍 平衡树 || root 合并
看到树上路径问题点分治肯定可以做
看了一下其他题解好像没有具体介绍如何不重不漏地计数以及一些细节(其实是我太菜了没看懂
蒟蒻我做点分题还没有用过容斥定理,一般都是在每颗子树里跑一遍的时候直接累加前面已经跑过的贡献QWQ
(后面会详细介绍如何累计答案
上代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100009;
typedef long long LL;
int n,point[N],head[N],cnt,siz[N],del[N],l[N],tmp,pool,root,w[N],tot;
LL ans,all[N],ans_1;
struct Edge
{
int nxt,to,w;
}g[2*N];
struct Tree
{
int pri,l,r,cnt,siz;
LL val;
#define p(x) a[x].pri
#define v(x) a[x].val
#define l(x) a[x].l
#define r(x) a[x].r
#define c(x) a[x].cnt
#define s(x) a[x].siz
}a[N*10];
void Zig(int &k)
{
int x=l(k);
l(k)=r(x);
r(x)=k;
s(x)=s(k);
s(k)=s(l(k))+s(r(k))+c(k);
k=x;
}
void Zag(int &k)
{
int x=r(k);
r(k)=l(x);
l(x)=k;
s(x)=s(k);
s(k)=s(l(k))+s(r(k))+c(k);
k=x;
}
void Insert(int &k,LL x)
{
if(!k)
{
k=++pool,v(k)=x,c(k)=s(k)=1,p(k)=rand();
return;
}
s(k)++;
if(x==v(k))
c(k)++;
else if(x<v(k))
{
Insert(l(k),x);
if(p(l(k))<p(k))
Zig(k);
}
else
{
Insert(r(k),x);
if(p(r(k))<p(k))
Zag(k);
}
}
void Delete(int &k,LL x)
{
if(v(k)==x)
{
if(c(k)>1)
c(k)--,s(k)--;
else if(!l(k)||!r(k))
k=l(k)+r(k);
else if(p(l(k))<p(r(k)))
Zig(k),Delete(k,x);
else
Zag(k),Delete(k,x);
return;
}
s(k)--;
if(x<v(k))
Delete(l(k),x);
else
Delete(r(k),x);
}
int Query(LL x)//计算当前Treap里>=x的数有多少个
{
int k=root,ans=0;
while(k)
{
if(x<v(k))
ans+=c(k)+s(r(k)),k=l(k);
else if(x>v(k))
k=r(k);
else
return ans+c(k)+s(r(k));
}
return ans;
}
void add(int from,int to,int w)
{
g[++cnt].nxt=head[from];
g[cnt].to=to;
g[cnt].w=w;
head[from]=cnt;
}
void init()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&point[i]);
int x,y,z;
for (int i=1;i<n;i++)
{
scanf("%d %d %d",&x,&y,&z);
add(x,y,z),add(y,x,z);
}
}
void dfs(int x,int fa)//计算子树大小
{
siz[x]=1;
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa||del[v])
continue;
dfs(v,x);
siz[x]+=siz[v];
}
}
int Get_Weight(int x)//找重心
{
dfs(x,-1);
int k=siz[x]/2,fa=-1;
while(1)
{
int tmp=0;
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(del[v]||v==fa)
continue;
if(siz[v]>siz[tmp])
tmp=v;
}
if(siz[tmp]<=k)
return x;
fa=x,x=tmp;
}
}
void DFS(int x,int fa,LL Max,LL now)//第一遍DFS
{
ans+=Query(Max);
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa||del[v])
continue;
DFS(v,x,max(Max,now-point[x]+g[i].w),now-point[x]+g[i].w);
}
}
void DFS_1(int x,int fa,LL Max,LL now)//第二遍DFS
{
if(now>=Max)
Insert(root,now),all[++tot]=now,ans_1++;
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa||del[v])
continue;
DFS_1(v,x,max(now,Max),now+point[v]-g[i].w);
}
}
void Get_Ans(int x)
{
tmp=tot=ans_1=0;
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(del[v])
continue;
l[++tmp]=v,w[tmp]=g[i].w;
}
//正着跑一遍
for (int i=1;i<=tmp;i++)
{
DFS(l[i],x,w[i],w[i]);
DFS_1(l[i],x,point[x],point[x]+point[l[i]]-w[i]);
}
while(tot)
Delete(root,all[tot--]);//清空防止对以后产生影响
//倒着跑一遍
Insert(root,point[x]);//分治中心的油量第二遍再加进来
for (int i=tmp;i>=1;i--)
{
DFS(l[i],x,w[i],w[i]);
DFS_1(l[i],x,point[x],point[x]+point[l[i]]-w[i]);
}
while(tot)
Delete(root,all[tot--]);
Delete(root,point[x]);
ans+=ans_1/2;//点对(子树节点,分治中心)的贡献最后要除以二
}
void conquer(int x)
{
int w=Get_Weight(x);
Get_Ans(w);
del[w]=1;
for (int i=head[w];i;i=g[i].nxt)
{
int v=g[i].to;
if(del[v])
continue;
conquer(v);
}
}
void work()
{
Insert(root,-(1LL<<60)),conquer(1);//加入负无穷大是为了怕Query出锅
printf("%lld\n",ans);
}
int main()
{
init();
work();
return 0;
}
luogu P5306 [COCI2019] Transport
标签:tran 就会 ios upd 介绍 平衡树 || root 合并
原文地址:https://www.cnblogs.com/With-penguin/p/13277354.html