P4183 [USACO18JAN] Cow at Large P
Genius_Star · · 题解
怎么题解区就没有一个不用容斥的
思路:
令
考虑先求出到达子树内的叶子节点的最短距离:
void dfs1(ll u,ll fa){
if(d[u]==1)
g[u]=0;
else
g[u]=INF;
for(auto v:E[u]){
if(v==fa)
continue;
dfs1(v,u);
g[u]=min(g[u],g[v]+1);
}
}
然后
void dfs2(ll u,ll fa){
for(auto v:E[u]){
if(v==fa)
continue;
g[v]=min(g[v],g[u]+1);
dfs2(v,u);
}
}
那么若一个点
注意到若点
即
暴力求解是
注意到满足
因为一棵树中的一个长度为
则对于一棵满足
现在问题转化为了求
对于当前的分治重心即根
那么点分治需要处理三种贡献:
考虑令
对于
那么可以在
但是我们注意到对于点对
对于
对于
注意到树状数组小标可能为负数,可以全体向右偏移
注意每次计算的时候不可以能遍历整个树状数组清空,需要维护一个时间戳,这样时间复杂度才能保证。
总时间复杂度为
完整代码:
#include<bits/stdc++.h>
#define lowbit(x) x&(-x)
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef long long ll;
const ll N=2e5+10,INF=1e9;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
ll n,sum,Max,root,cnt,low;
ll a[N],b[N],ans[N],g[N],d[N],dep[N],siz[N],dis[N];
bool del[N];
vector<ll> E[N];
void add(ll u,ll v){
d[u]++,d[v]++;
E[u].push_back(v);
E[v].push_back(u);
}
void Add(ll x,ll v){
x+=n;
for(int i=x;i<=2*n;i+=lowbit(i)){
if(b[i]!=low){
b[i]=low;
a[i]=0;
}
a[i]+=v;
}
}
ll query(ll x){
x+=n;
ll ans=0;
for(int i=x;i;i-=lowbit(i)){
if(b[i]!=low){
b[i]=low;
a[i]=0;
}
ans+=a[i];
}
return ans;
}
void dfs1(ll u,ll fa){
if(d[u]==1)
g[u]=0;
else
g[u]=INF;
for(auto v:E[u]){
if(v==fa)
continue;
dfs1(v,u);
g[u]=min(g[u],g[v]+1);
}
}
void dfs2(ll u,ll fa){
for(auto v:E[u]){
if(v==fa)
continue;
g[v]=min(g[v],g[u]+1);
dfs2(v,u);
}
}
void getroot(ll u,ll fa){
ll s=0;
siz[u]=1;
for(auto v:E[u]){
if(v==fa||del[v])
continue;
getroot(v,u);
siz[u]+=siz[v];
s=max(s,siz[v]);
}
s=max(s,sum-siz[u]);
if(s<Max){
Max=s;
root=u;
}
}
void Get(ll u,ll s){
root=0;
sum=Max=s;
getroot(u,0);
getroot(root,0);
}
void getdis(ll u,ll fa){
dis[++cnt]=u;
for(auto v:E[u]){
if(v==fa||del[v])
continue;
dep[v]=dep[u]+1;
getdis(v,u);
}
}
void Calc(ll u,bool f){
++low;
dep[u]=0;
if(f)
Add(g[u]-dep[u],2-d[u]);
for(auto v:E[u]){
if(del[v])
continue;
cnt=0;
dep[v]=1;
getdis(v,u);
for(int i=1;i<=cnt;i++)
ans[dis[i]]+=query(dep[dis[i]]);
for(int i=1;i<=cnt;i++)
Add(g[dis[i]]-dep[dis[i]],2-d[dis[i]]);
}
if(!f)
ans[u]+=query(0);
}
void calc(ll u){
Calc(u,1);
reverse(E[u].begin(),E[u].end());
Calc(u,0);
}
void solve(ll u){
calc(u);
del[u]=1;
for(auto v:E[u]){
if(del[v])
continue;
Get(v,siz[v]);
solve(root);
}
}
int main(){
n=read();
for(int u,v,i=1;i<n;i++){
u=read(),v=read();
add(u,v);
}
dfs1(1,0);
dfs2(1,0);
Get(1,n);
solve(root);
for(int i=1;i<=n;i++){
write(ans[i]);
putchar('\n');
}
return 0;
}