[Hackerrank题目选做] 出租车司机问题数据结构+树分治

xiaoxiao2021-02-28  62

题目链接

题目大意:给一棵树,每条边有两个权值ai和bi,

                    当路径上sigma(ai)和sigma(bi)都不超过限制la,lb时,点对(i,j)可达,

                    求不可达的无序点对(i,j)的数量。

数据范围:N<=10^5,ai,bi<=10^9,la,lb<=10^14

题解:树上的路径问题,这次树形dp无法解决这道题了,

            于是考虑利用树分治解决树上路径问题,

            每次找出重心进行树分治后,

            如果对a,b用二维数据结构强行维护,可以O(nlog^3n)解决,

            然而这样不仅代码复杂度高,而且会被某出题人无情卡T,

            因为有两个log后面的数字达到了10^14 。

            再考虑一些更加巧妙的办法-------->

            离线后,扫描线+树状数组维护,

            对于每个点, 通过树分治后的扫描(树形dp), 我们得到了三个特征值:ai,bi,ci,

            其中,ai,bi分别表示到根节点的a,b两种权值的权值和,ci表示这个点所在的子树,

            这个点的询问是(la-ai,lb-bi,c),就是满足aj<=la-ai,bj<=lb-bi,cj!=ci的j的个数,

            将每个点产生的三元组先进行离散化,

            然后它们进行排序后利用树状数组进行询问,

            树状数组维护时,维护一个总体的树状数组,对于每个颜色再维护一个树状数组,

            计算答案时,对两个树状数组分别询问后作差即为对答案的贡献,

            注意,因为ci可能达到O(n)种,所以树状数组要用vector开。

            然后,就可以得到一个常数很小的O(nlog^2n)的树分治+离线后扫描线,离散化+树状数组的解法。

Code: 

#include <bits/stdc++.h> #define ll long long using namespace std; ll x[100005],y[100005],lx,ly,ans=0; int head[100005],nxt[200005],v[200005],tot=0; int ntp,cnt=0; ll w1[200005],w2[200005]; ll dx[100005],dy[100005]; bool vis[100005]; int maxn[100005],sz[100005],n,rt,minn=1000000,mpos=-1; struct node {int typ,c; ll x,y; }q[200005]; inline bool cmp(node a,node b) {if (a.x!=b.x) {return a.x<b.x;} if (a.y!=b.y) {return a.y<b.y;} return a.typ<b.typ; } struct fenwick {vector <ll> t; vector <ll> a; void clear() {t.clear(); a.clear(); } void add(ll x) {a.push_back(x); } void init() {sort(a.begin(),a.end()); t.assign(a.size()+1,0); } void update (ll x) {int pos=(lower_bound(a.begin(),a.end(),x)-a.begin()); pos++; while (pos<=a.size()+1) {t[pos]++; pos+=(pos&(-pos)); } } ll query (ll val) {int pos=(lower_bound(a.begin(),a.end(),val)-a.begin()); pos++; ll s=0; while (pos) {s+=t[pos]; pos-=(pos&(-pos)); } return s; } }f[100005]; inline ll my_abs(ll a) {if (a<0) return -a; return a; } inline void add(int a,int b,ll v1,ll v2) {tot++; nxt[tot]=head[a]; head[a]=tot; v[tot]=b; w1[tot]=v1; w2[tot]=v2; } void dfs1(int pos,int fa) {sz[pos]=1;maxn[pos]=0; for (int i=head[pos];i;i=nxt[i]) {if (v[i]!=fa&&!vis[v[i]]) {dfs1(v[i],pos); sz[pos]+=sz[v[i]]; maxn[pos]=max(maxn[pos],sz[v[i]]); } } } void dfs2(int pos,int fa) {maxn[pos]=max(maxn[pos],sz[rt]-sz[pos]); if (maxn[pos]<minn) {minn=maxn[pos];mpos=pos;} for (int i=head[pos];i;i=nxt[i]) {if (v[i]!=fa&&!vis[v[i]]) {dfs2(v[i],pos);} } } void dp(int pos,int fa,int typ) {cnt++; q[cnt].typ=1; q[cnt].c=typ; q[cnt].x=dx[pos]; q[cnt].y=dy[pos]; f[typ].add(dy[pos]); f[0].add(dy[pos]); cnt++; q[cnt].typ=2; q[cnt].c=typ; q[cnt].x=lx-dx[pos]; q[cnt].y=ly-dy[pos]; f[typ].add(ly-dy[pos]); f[0].add(ly-dy[pos]); for (int i=head[pos];i;i=nxt[i]) {if (!vis[v[i]]&&v[i]!=fa) {if (!fa) {typ=++ntp; f[ntp].clear(); } dx[v[i]]=dx[pos]+w1[i]; dy[v[i]]=dy[pos]+w2[i]; dp(v[i],pos,typ); } } } void solve(int pos) {int i; rt=pos; minn=1000000; mpos=-1; dfs1(pos,0); dfs2(pos,0); vis[mpos]=1; dx[mpos]=0; dy[mpos]=0; ntp=0;cnt=0; f[0].clear(); dp(mpos,0,0); for (i=0;i<=ntp;i++) {f[i].init();} sort(q+1,q+cnt+1,cmp); for (i=1;i<=cnt;i++) {if (q[i].typ==1) {if (q[i].c) {f[q[i].c].update(q[i].y);} f[0].update(q[i].y); } else {ans+=f[0].query(q[i].y); if (q[i].c) {ans-=f[q[i].c].query(q[i].y);} } } ans--; for (i=head[mpos];i;i=nxt[i]) {if (!vis[v[i]]) {solve(v[i]);} } return; } int main (){ int i,u,v; scanf ("%d",&n); scanf ("%lld%lld",&lx,&ly); for (i=1;i<=n;i++) {scanf ("%lld%lld",&x[i],&y[i]);} for (i=1;i<n;i++) {scanf ("%d%d",&u,&v); ll delx=my_abs(x[u]-x[v]); ll dely=my_abs(y[u]-y[v]); add(u,v,delx,dely); add(v,u,delx,dely); } solve(1); ans=(((ll)(n))*((ll)(n-1)))-ans; ans/=2ll; printf ("%lld\n",ans); return 0; }

           

           

转载请注明原文地址: https://www.6miu.com/read-84237.html

最新回复(0)