https://codeforces.com/gym/102452/problem/C
这种多少条满足条件的路径一眼就是点分治题,然而我半年多没写不太会了。。。还好告诉了队友后队友一个小时1A了,补一下这题复习一下点分治,不能总是依赖队友。。。
首先能组成简单多边形的条件就是sum>2*mx,可以由三角形两边之和大于第3边类比过来。
由于这题的边长是1e9级别的,所以我们要用用树状数组维护。我们把没有标记为true的子树拿出来,从根节点到每个点的路径长度sum和路径上的最大值mx求出来,然后把所有路径长度sum拿出来离散化一下,然后对mx排序,依次添加到树状数组里面,然后树状数组中保存的路径都最大值<当前枚举的路径的mx,所以可以直接在离散化权值数组中upperbound一下找到恰好满足的位置,然后就可以用前缀和求出有多少可以跟当前路径组成满足条件的路径了。
由于是要离散化的,所以最好用容斥的写法,可以求出所有路径长度拿出来容斥
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> p;
const int maxl=2e5+10;
int n,len,tot;ll ans;
ll a[maxl],num[maxl],b[maxl],dis[maxl];
vector<int> e[maxl];
vector<p> pth;
bool vis[maxl];
struct ctr
{
int ans,n,mi;
int son[maxl];
inline void dfs(int u,int fa)
{
son[u]=1;int ret=0;
for(int v:e[u])
if(!vis[v] && v!=fa)
{
dfs(v,u);
son[u]+=son[v];
ret=max(son[v],ret);
}
ret=max(ret,n-son[u]);
if(ret<mi)
{
ans=u;
mi=ret;
}
}
inline int getct(int u)
{
ans=0;mi=2e9;
dfs(u,0);
return ans;
}
}ct;
inline void prework()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),e[i].clear(),vis[i]=false;
int u,v;
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
}
inline ll getsum(int i)
{
ll ret=0;
while(i)
{
ret+=b[i];
i-=i&-i;
}
return ret;
}
inline void add(int i,ll val,int n)
{
while(i<=n)
{
b[i]+=val;
i+=i&-i;
}
}
inline void getval(int u,ll mx,ll sum,int fa)
{
mx=max(mx,a[u]);sum+=a[u];
pth.push_back({mx,sum});
for(int v:e[u])
if(!vis[v] && v!=fa)
getval(v,mx,sum,u);
}
inline ll calc(int u,ll w,int fa)
{
tot=0;pth.clear();
getval(u,w,w,fa);
for(p d:pth)
num[++tot]=d.second;
sort(num+1,num+1+tot);
tot=unique(num+1,num+1+tot)-num-1;
sort(pth.begin(),pth.end());
ll mx,sum,val=(w>0)?w:a[u],ret=0;int pos;
for(p d:pth)
{
mx=d.first;sum=d.second;
pos=upper_bound(num+1,num+1+tot,2*mx-sum+val)-num;
if(pos<=tot)
ret+=getsum(tot)-getsum(pos-1);
pos=lower_bound(num+1,num+1+tot,sum)-num;
add(pos,1ll,tot);
}
for(p d:pth)
{
sum=d.second;
pos=lower_bound(num+1,num+1+tot,sum)-num;
add(pos,-1ll,tot);
}
return ret;
}
inline void solv(int u)
{
vis[u]=true;ans+=calc(u,0ll,0);
int rt;
for(int v:e[u])
if(!vis[v])
{
ans-=calc(v,a[u],u);
ct.n=ct.son[v];
rt=ct.getct(v);
solv(rt);
}
}
inline void mainwork()
{
ct.n=n;
int rt=ct.getct(1);
ans=0;
solv(rt);
}
inline void print()
{
printf("%lld\n",ans);
}
int main()
{
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
prework();
mainwork();
print();
}
return 0;
}