[Codeforces 739B] Alyona and a tree | dsu on a tree, multiplication, dichotomy, tree difference, multiple solutions to one problem

Title Link: https://codeforces.com/contest/739/problem/B

Main idea of the title:

Given a tree, if V is the child node of u and dis (U, V) < = a [v], it is called u and can be controlled to v

Output the number of nodes that each node can control

Topic idea:

After reading the question, first deduce the formula:

Assuming that V is the child of u, dis(u,v) can be expressed as: deep[v] - deep[u], and deep is the weighted depth

So from deep[v] - deep [u] < = a [v], then for any point u, find out how many nodes in the subtree meet, and deep [u] < = deep[v] - a [v]

Idea 1:

Tree section + chairman tree is OK

The chairman tree takes deep[u] - a[v] as the insertion point. In this case, an offset base needs to be added

The code will not be released. Just set the chairman tree template

Complexity: About O(n*50)

Idea 2:

dsu on a tree

Obviously, this is a problem of static subtree information statistics. For a subtree, you can count all the information of deep[v]-a[v]. Finally, you can query how many are less than or equal to deep[u]

Originally written code, with unordered_map maintains a tree array

Complexity: O(n*logn*logn*C) C is unordered_ Constant of map

Sure enough, TLE37

At this time, in order to reduce C and the second log, it is discretized to control the complexity to nlog^2

1060ms

Code:

/*** keep hungry and calm CoolGuang!***/
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define d(x) printf("%lld\n",x);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll INF= 1e17;
const ll maxn = 1e6+700;
const int mod= 1e9+7;
template<typename T>inline void read(T &a){char c=getchar();T x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
ll n,m,p;
int sz[maxn],son[maxn];
ll a[maxn],num[maxn],deep[maxn];
vector<pair<int,ll>>v[maxn];
ll sum[maxn];
vector<ll>g;
int nn;
ll nmax = 0,base = 0;
void dfs(int u,int fa,ll w){
    sz[u] = 1;
    deep[u] = deep[fa] + w;
    for(auto x:v[u]){
        int e = x.first;
        if(e == fa) continue;
        dfs(e,u,x.second);
        sz[u] += sz[e];
        if(sz[son[u]]<sz[e]) son[u] = e;
    }
}
int getid(ll x){
    return lower_bound(g.begin(),g.end(),x) - g.begin() + 1;
}
ll GetSum(ll pos){
    ll ans = 0;
    while(pos){
        ans += sum[pos];
        pos -= pos&-pos;
    }return ans;
}
void Modify(ll pos,int w){
    while(pos <= nn){
        sum[pos] += w;
        pos += pos&-pos;
    }
}
void cal(int u,int fa,int f){
    Modify(getid(deep[u]-num[u]),f);
    for(auto x:v[u]){
        int e = x.first;
        if(e == fa) continue;
        cal(e,fa,f);
    }
}
void solve(int u,int fa,int del){
    for(auto x:v[u]){
        int e = x.first;
        if(e == son[u]) continue;
        solve(e,u,1);
    }
    if(son[u]) solve(son[u],u,0);
    for(auto x:v[u]){
        int e = x.first;
        if(e == son[u]) continue;
        cal(e,u,1);
    }
    a[u] = GetSum(getid(deep[u]));
    Modify(getid(deep[u]-num[u]),1);
    if(del) cal(u,fa,-1);
}
int main(){
    read(n);
    for(int i=1;i<=n;i++) read(num[i]);
    for(int i=2;i<=n;i++){
        ll x,y;read(x);read(y);
        v[x].push_back({i,y});
    }
    dfs(1,1,0);

    for(int i=1;i<=n;i++){
        g.push_back(deep[i] - num[i]);
        g.push_back(deep[i]);
    }
    sort(g.begin(),g.end());
    g.erase((unique(g.begin(),g.end())),g.end());
    nn = g.size();

    solve(1,1,0);
    for(int i=1;i<=n;i++) printf("%lld ",a[i]);
    return 0;
}
/***
5 5 3
1 2 3 4 5
3 3
3 2
1 5
***/

Idea 3:

Consider a node that can be controlled to be his father node

Then you can find the node x that finally meets one condition in the path from this point to the root node, and then make a difference in the tree

As for how to find this x node, you can consider doubling to ensure that this node is found within the complexity of the log

Finally, dfs traversal can be restored

560ms

Code:

/*** keep hungry and calm CoolGuang!***/
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define d(x) printf("%lld\n",x);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll INF= 1e17;
const ll maxn = 2e5+700;
const int mod= 1e9+7;
template<typename T>inline void read(T &a){char c=getchar();T x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
ll n,m,p;
vector<pair<int,ll>>v[maxn];
int f[maxn][23];
ll sum[maxn][23];
ll res[maxn],a[maxn],num[maxn];
void dfs(int u,int fa,ll w){
    if(u != fa){
        f[u][0] = fa;
        sum[u][0] = w;
        for(int i=1;i<=20;i++){
            f[u][i] = f[f[u][i-1]][i-1];
            sum[u][i] = sum[u][i-1] + sum[f[u][i-1]][i-1];
        }
        ll  p = u,temp = num[u];
        for(int i=20;i>=0;i--){
            if(f[p][i] && sum[p][i] <= temp){
                temp -= sum[p][i];
                p = f[p][i];
            }
        }
        res[f[p][0]]--;
        res[f[u][0]]++;
    }
    for(auto x:v[u]){
        int e = x.first;
        if(e == fa) continue;
        dfs(e,u,x.second);
    }
}
ll ans = 0;
void work(int u,int fa){
    a[u] += res[u];
    for(auto x:v[u]){
        int e = x.first;
        if(e == fa) continue;
        work(e,u);
        a[u] += a[e];
    }
}
int main(){
    read(n);
    for(int i=1;i<=n;i++) read(num[i]);
    for(int i=2;i<=n;i++){
        ll x,y;read(x);read(y);
        v[x].push_back({i,y});
    }
    dfs(1,1,0);
    work(1,1);
    for(int i=1;i<=n;i++) printf("%lld ",a[i]);
    return 0;
}
/***
5 5 3
1 2 3 4 5
3 3
3 2
1 5
***/

Idea 4:

The third way of thinking can be optimized without doubling. Because the depth is absolutely increasing, it can be divided into two points

After the node is found, the difference is performed again

As for how to split, just look at the code

It has to be Du Jiao

365ms

Code:

/*** keep hungry and calm CoolGuang!***/
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define d(x) printf("%lld\n",x);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll INF= 1e17;
const ll maxn = 2e5+700;
const int mod= 1e9+7;
template<typename T>inline void read(T &a){char c=getchar();T x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
ll n,m,p;
vector<pair<int,ll>>v[maxn];
int f[maxn][23];
ll d[maxn];
ll res[maxn],a[maxn];
int sta[maxn];
ll num[maxn];
void dfs(int u,int fa,ll w,int deep){
    sta[deep] = u;d[deep] = w;
    int l = lower_bound(d+1,d+1+deep,w-num[u]) - d - 1;
    res[sta[l]]--;res[u]++;
    for(auto x:v[u]){
        int e = x.first;
        if(e == fa) continue;
        dfs(e,u,w+x.second,deep+1);
    }
}
ll ans = 0;
void work(int u,int fa){
    a[u] += res[u];
    for(auto x:v[u]){
        int e = x.first;
        if(e == fa) continue;
        work(e,u);
        a[u] += a[e];
    }
}
int main(){
    read(n);
    for(int i=1;i<=n;i++) read(num[i]);
    for(int i=2;i<=n;i++){
        ll x,y;read(x);read(y);
        v[x].push_back({i,y});
    }
    dfs(1,1,0,1);
    work(1,1);
    for(int i=1;i<=n;i++) printf("%lld ",a[i]-1);
    return 0;
}
/***
5 5 3
1 2 3 4 5
3 3
3 2
1 5
***/

 

Posted by metalblend on Wed, 04 May 2022 06:09:12 +0300