2019香港区域赛gym102452 C. Constructing Ranches(点分治)

Ranching and the cowboy tradition originated in Spain, out of the necessity to handle large herds of grazing animals on dry land from horseback. During the Reconquista, members of the Spanish nobility and various military orders received large land grants that the Kingdom of Castile had conquered from the Moors. These landowners were to defend the lands put into their control and could use them for earning revenue. In the process, it was found that open-range breeding of sheep and cattle (under the Mesta system) was the most suitable use for vast tracts, particularly in the parts of Spain now known as Castilla-La Mancha, Extremadura and Andalusia.

The historic 101 Ranch in Oklahoma, US. Public domain.
Jace is an employee at the International Cattle Production Company (ICPC), whose mission today is to help his client Karn to build a new cattle ranch. Both Jace and Karn agree that the ranch should be surrounded by fences to ensure security, and the shape of the ranch should be a simple polygon. Recall that a simple polygon is a polygon that does not intersect itself and has no holes. That is, it is a flat shape consisting of straight, non-intersecting line segments that are joined to form a single closed path. A simple polygon always has a measurable and strictly positive area.

There are 𝑛 shops selling fence segments in the town where Karn lives, they are numbered from 1 to 𝑛 for convenience. Exactly 𝑛−1 bidirectional roads are connecting the shops, and there is exactly one simple path to travel between any two shops using those roads. In other words, the shops and roads form a tree in graph theory. The 𝑖-th shop only has a single fence segment for sale, whose length is 𝑎𝑖. Jace plans to travel from one shop 𝑥 to another shop 𝑦, and buy all fence segments from the shops on the only simple path from 𝑥 to 𝑦 (including 𝑥 and 𝑦). Then, he will try to build the fence (as mentioned above, it must be a simple polygon) with the segments he has bought. Since Karn doesn’t want to waste any money, Jace must use all the segments in the fence. Please help Jace calculate how many pairs (𝑥,𝑦) are there such that 𝑥<𝑦, and Jace can build a valid fence if he travels from 𝑥 to 𝑦.

Input
The input contains multiple cases. The first line of the input contains a single positive integer 𝑇, the number of cases.

For each case, the first line of the input contains a single integer 𝑛 (1≤𝑛≤2⋅105), the number of shops. The second line contains 𝑛 integers, where the 𝑖-th (1≤𝑖≤𝑛) integer 𝑎𝑖 (1≤𝑎𝑖≤109) denotes the length of the fence segment on sale at the 𝑖-th shop. The following 𝑛−1 lines each contains two integers 𝑢,𝑣 (1≤𝑢,𝑣≤𝑛), denoting a bidirectional road between shop 𝑢 and shop 𝑣. It is guaranteed that the shops and roads form a tree.

It is guaranteed that the sum of 𝑛 over all cases doesn’t exceed 4⋅105.

Output
For each case, print a single integer in a single line, the number of valid pairs (𝑥,𝑦).

Example
inputCopy
2
3
1 10 100
1 2
3 2
5
1 1 1 1 1
1 2
1 3
1 4
1 5
outputCopy
0
6
Note
In the second sample case, the following pairs are valid: (2,3), (2,4), (2,5), (3,4), (3,5), (4,5).

题意:
点有点权,一棵树上多少路径,所有点的点权能构成一个多边形

思路:
构成多边形的条件是,其他边的和大于最大边。

所以对于对于一条路径已知当前点权和sum与最大值mx,那么点权和还能再增加2*mx-sum的数目。我们可以将所有路径的点权和与最大值存下来,然后按照mx排序,对每个路径判断可以匹配多少其他路径,这个过程可以用二分维护。

路径的种类可以分为过当前根节点的,在子节点里面的,算完根节点的,要减去子节点里面路径互相组合的结果,用点分治就好了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <unordered_map>
#include <map>
#include <queue>
#include <cstdlib>
#include <ctime>

using namespace std;

typedef long long ll;
const int maxn = 2e5 + 7;

ll a[maxn];
vector<int>G[maxn];
int vis[maxn];

///找重心
int siz[maxn];
int pos,ans_part;
void dfs_find(int s,int x,int fa) {
    int max_part = 0;
    siz[x] = 1;
    for(int i = 0;i < G[x].size();i++) {
        int v = G[x][i];
        if(vis[v] || v == fa) {
            continue;
        }
        dfs_find(s,v,x);
        siz[x] += siz[v];
        max_part = max(max_part,siz[v]);
    }
    max_part = max(max_part,s - siz[x]);
    if(max_part < ans_part) {
        ans_part = max_part;
        pos = x;
    }
}

///树状数组
ll c[maxn];

void add(int x,int v) {
    while(x < maxn) {
        c[x] += v;
        x += x & -x;
    }
}

ll query(int x) {
    ll res = 0;
    while(x) {
        res += c[x];
        x -= x & -x;
    }
    return res;
}

//寻找以x为根节点的所有路径的和和最大值
vector<pair<ll,ll> >path;
void dfs(int x,int fa,ll mx,ll sum) {
    mx = max(mx,a[x]);
    sum += a[x];
    path.push_back({mx,sum});
    for(int i = 0;i < G[x].size();i++) {
        int v = G[x][i];
        if(v == fa || vis[v]) continue;
        dfs(v,x,mx,sum);
    }
}

//树上分治统计
ll ans;
ll num[maxn];

ll solve(int u,int fa,ll w) {
    path.clear();
    dfs(u,fa,w,w);
    int tot = 0;
    for(int i = 0;i < path.size();i++) {
        num[++tot] = path[i].second;
    }
    sort(num + 1,num + 1 + tot);
    tot = unique(num + 1,num + 1 + tot) - (num + 1);
    
    sort(path.begin(),path.end());
    
    ll val = w ? w : a[u];
    ll res = 0;
    
    for(int i = 0;i < path.size();i++) {
        ll mx = path[i].first,sum = path[i].second;
        int p = upper_bound(num + 1,num + 1 + tot,2 * mx - sum + val) - num;
        if(p <= tot) {
            res += query(tot) - query(p - 1);
        }
        p = lower_bound(num + 1,num + 1 + tot,sum) - num;
        add(p,1);
    }
    
    for(int i = 0;i < path.size();i++) {
        ll sum = path[i].second;
        int p = lower_bound(num + 1,num + 1 + tot,sum) - num;
        add(p,-1);
    }
    return res;
}

void work(int s,int x) {
    ans_part = 1e9;
    pos = 0;
   
    dfs_find(s,x,0); //寻找重心
    vis[pos] = 1;
    
    ans += solve(pos,0,0);
    int now = pos;
    dfs_find(s,pos,0);
    for(int i = 0;i < G[now].size();i++) {
        int v = G[now][i];
        if(vis[v]) continue;
        ans -= solve(v,now,a[now]);
        work(siz[v],v);
    }
}

int main() {
    int T;scanf("%d",&T);
    while(T--) {
        int n;scanf("%d",&n);
        for(int i = 1;i <= n;i++) {
            scanf("%lld",&a[i]);
            G[i].clear();
        }
        for(int i = 1;i < n;i++) {
            int x,y;scanf("%d%d",&x,&y);
            G[x].push_back(y);
            G[y].push_back(x);
        }
        ans = 0;
        memset(vis,0,sizeof(vis));
        
        work(n,1);
        printf("%lld\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值