《洛谷P3806》

也是点分治的简单题。(对我来说算难了orz.)

 首先这里的难点就在于查询是否存在长度k。

如果我们每次都去对询问进行一次点分治,显然是过于浪费了。

所以我们可以离线做它。

在点分治过程中统计所有答案。

那么怎么统计答案,又是一大难点,如果要直接按之前找k点对的统计。

那么容斥删点的时候,需要找遍所有的组合,复杂度就到了n^2.显然不行。

这里就采用了新的思路,不再容斥。

我们对每个点是在哪个子节点的下面都做一个标记,然后再去统计即可。

如果在同一个子节点里,说明不合法。

然后对每个询问都进行一次类似二分的查询即可。

复杂度应该接近nlogn

// Author: levil
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N = 1e4+5;
const int M = 1e7+5;
const LL Mod = 1e9+7;
#define rg register
#define pi acos(-1)
#define INF 1e18
#define CT0 cin.tie(0),cout.tie(0)
#define IO ios::sync_with_stdio(false)
#define dbg(ax) cout << "now this num is " << ax << endl;
namespace FASTIO{
    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;
    }
    void print(int x){
        if(x < 0){x = -x;putchar('-');}
        if(x > 9) print(x/10);
        putchar(x%10+'0');
    }
}
using namespace FASTIO;
void FRE(){
/*freopen("data1.in","r",stdin);
freopen("date1.out","w",stdout);*/}

int n,m,cnt = 0,head[N],ask[105],vis[N],ssize[N],mx = INF,rt,sz = n,tot = 0,val[105];
int dis[N],a[N],b[N];//dis[i]距离i的距离,a[i]i哪个点,b[i]-i为在哪个子节点
struct Node{int to,dis,next;}e[N<<1];
inline void add(int u,int v,int w)
{
    e[++cnt].to = v,e[cnt].dis = w,e[cnt].next = head[u],head[u] = cnt;
}
bool cmp(int a,int b)
{
    return dis[a] < dis[b];
}
void Findroot(int u,int fa)
{
    ssize[u] = 1;
    int son = 0;
    for(rg int i = head[u];i;i = e[i].next)
    {
        int v = e[i].to,d = e[i].dis;
        if(v == fa || vis[v]) continue;
        Findroot(v,u);
        ssize[u] += ssize[v];
        son = max(son,ssize[v]);
    }
    son = max(son,sz-ssize[u]);
    if(son < mx) mx = son,rt = u;
}
void Dis_slove(int u,int fa,int root,LL z)
{
    a[++tot] = u;
    dis[u] = z;
    b[u] = root;
    for(rg int i = head[u];i;i = e[i].next)
    {
        int v = e[i].to,d = e[i].dis;
        if(v == fa || vis[v]) continue;
        Dis_slove(v,u,root,z+d);
    }
}
void slove(int u)
{
    tot = 0;
    a[++tot] = u;
    dis[u] = 0;
    b[u] = u;
    for(rg int i = head[u];i;i = e[i].next)
    {
        int v = e[i].to,d = e[i].dis;
        if(vis[v]) continue;
        Dis_slove(v,u,v,d);
    }
    sort(a+1,a+tot+1,cmp);
    for(rg int i = 1;i <= m;++i)
    {
        if(ask[i]) continue;
        int L = 1,r = tot;
        while(L < r)
        {
            if(dis[a[L]]+dis[a[r]] > val[i]) r--;
            else if(dis[a[L]]+dis[a[r]] < val[i]) L++;
            else//长度相等
            {
                if(b[a[L]] == b[a[r]])//如果两个点都属于同一子节点,那么关于L的查询已经结束,因为L不可能再有了,所以移动L,但是如果r-1=r,那么r-1也不用查询了,直接移动r
                {
                    if(dis[a[r]] == dis[a[r-1]]) r--;//
                    else L++;
                }
                else{ask[i] = 1;break;}
            }
        }
    }
}
void divide(int u)
{
    slove(u);
    vis[u] = 1;
    for(rg int i = head[u];i;i = e[i].next)
    {
        int v = e[i].to,d = e[i].dis;
        if(vis[v]) continue;
        sz = ssize[v],mx = INF;
        Findroot(v,0);
        divide(rt);
    }
}
int main()
{
    n = read(),m = read();
    for(rg int i = 1;i < n;++i)
    {
        int u,v,w;
        u = read(),v = read(),w = read();
        add(u,v,w);add(v,u,w);
    }
    for(rg int i = 1;i <= m;++i) 
    {
        val[i] = read();
        if(val[i] == 0) ask[i] = 1;
    }
    Findroot(1,0);
    divide(rt);
    for(rg int i = 1;i <= m;++i) printf("%s
",ask[i] ? "AYE" : "NAY");
    system("pause");     
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/zwjzwj/p/13571152.html