OI时期曾用过dfs序,结果今天没想起来。。。。考场上yy树状数组特殊用法奇特姿势,然而没卵用,只能dfs序转成区间。一场比赛A 2题,哇我好弱啊。。
给你一棵树 每个结点有r,t值 现在求所有结点i的子树中的r值小于i点的r值的所有结点的t值之和。
dfs序遍历,记录每个点访问进去时的dfn,和出来时候的dfn就知道他的子树的区间了。然后按照rank的从小到大进行添加,因为必须是严格小于时能加上t,所以dep小的先加,这样他的子树中可能和他rank相同的点就不会影响他了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxl 100010
using namespace std;
long long n,dfn,cnt,root;
struct node
{
long long rnk,ti,ind,dep;
}a[maxl];
long long intime[maxl],outtime[maxl],ehead[maxl],ans[maxl],b[maxl];
struct ed{long long nxt,to;} e[maxl];
bool vis[maxl];
void dfs(long long u,long long de)
{
long long v;
intime[u]=++dfn;vis[u]=true;a[u].dep=de;
for(long long i=ehead[u];i;i=e[i].nxt)
{
v=e[i].to;
if(!vis[v])
dfs(v,de+1);
}
outtime[u]=dfn;
}
void prework()
{
long long fa;dfn=0;cnt=0;
memset(ehead,0,sizeof(ehead));
for(long long i=1;i<=n;i++)
{
scanf("%I64d%I64d%I64d",&fa,&a[i].rnk,&a[i].ti);
a[i].ind=i;
if(fa>0)
e[++cnt].to=i,e[cnt].nxt=ehead[fa],ehead[fa]=cnt;
else
root=i;
}
memset(vis,false,sizeof(vis));
dfs(root,1);
}
bool cmp(const node &a,const node &b)
{
if(a.rnk==b.rnk)
return a.dep<b.dep;
else
return a.rnk<b.rnk;
}
long long sum(long long i)
{
long long s=0;
while(i)
{
s+=b[i];
i-=i&-i;
}
return s;
}
void add(long long i,long long x)
{
while(i<maxl)
{
b[i]+=x;
i+=i&-i;
}
}
void mainwork()
{
memset(ans,0,sizeof(ans));
memset(b,0,sizeof(b));
sort(a+1,a+1+n,cmp);
long long l,r;
for(long long i=1;i<=n;i++)
{
l=intime[a[i].ind];r=outtime[a[i].ind];
ans[a[i].ind]+=sum(r)-sum(l-1);
add(l,a[i].ti);
}
}
void print()
{
for(long long i=1;i<=n;i++)
printf("%I64d\n",ans[i]);
}
int main()
{
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
while(~scanf("%I64d",&n))
{
prework();
mainwork();
print();
}
return 0;
}