test20180922 打铁的匠

题意

分析

法一:吉司机线段树

这是一个在线的\(O( n + q \cdot \log^2 n)\)做法。
考虑维护节点到根的权值前缀和cost,那么查询的时候区间减去子树根节点的cost就是价值。
然后由于子树dfs序连续,转化成线段树的区间查询。

对区间查询,分为4种情况:

  1. 最大值都无价值,答案为0
  2. 最大值有价值,但次大值以后都无价值,那么答案就是最大值价值乘以最大值个数
  3. 最小值有价值,那么所有的都有价值,答案是sum-cost*区间长
  4. 以上条件都不满足,递归处理
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<ctime>
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<complex>
#pragma GCC optimize ("O0")
using namespace std;
template<class T> inline T read(T&x)
{
    T data=0;
	int w=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
		if(ch=='-')
			w=-1;
		ch=getchar();
	}
    while(isdigit(ch))
        data=10*data+ch-'0',ch=getchar();
    return x=data*w;
}
typedef long long ll;
const int INF=0x7fffffff;

const int MAXN=1e5+7;

struct Edge
{
	int nx,to,w;
}E[MAXN];
int head[MAXN],ecnt;

void addedge(int x,int y,int w)
{
	E[++ecnt].to=y,E[ecnt].w=w;
	E[ecnt].nx=head[x],head[x]=ecnt;
}

int dfn[MAXN],siz[MAXN],cost[MAXN];
int idx[MAXN],clk;

void dfs(int x)
{
//	cerr<<"dfsing "<<x<<endl;
	dfn[x]=++clk;
	siz[x]=1;
	idx[clk]=x;
	for(int i=head[x];i;i=E[i].nx)
	{
		int y=E[i].to,w=E[i].w;
		cost[y]=cost[x]+w;
		dfs(y);
		siz[x]+=siz[y];
	}
}

int ql,qr,c,k;
struct SegTree
{
	ll sumv[MAXN<<2];
	int maxv[MAXN<<2],num[MAXN<<2],secv[MAXN<<2];
	int minv[MAXN<<2];
#define lson (now<<1)
#define rson (now<<1|1)
	void pushup(int now)
	{
		sumv[now]=sumv[lson]+sumv[rson];
		maxv[now]=max(maxv[lson],maxv[rson]);
		minv[now]=min(minv[lson],minv[rson]);
		if(maxv[lson]==maxv[rson])
		{
			num[now]=num[lson]+num[rson];
			secv[now]=max(secv[lson],secv[rson]);
		}
		else
		{
			num[now]=maxv[lson]>maxv[rson]?num[lson]:num[rson];
			secv[now]=max(secv[lson],secv[rson]);
			secv[now]=max(secv[now],min(maxv[lson],maxv[rson]));
		}
	}
	
	void build(int now,int l,int r)
	{
		if(l==r)
		{
			sumv[now]=cost[idx[l]];
			maxv[now]=minv[now]=sumv[now];
			num[now]=1;
			secv[now]=-1;
			return;
		}
		int mid=(l+r)>>1;
		build(lson,l,mid);
		build(rson,mid+1,r);
		pushup(now);
	}
	
	ll query(int now,int l,int r)
	{
		if(ql<=l&&r<=qr)
		{
			if(maxv[now]<c+k)
				return 0;
			if(secv[now]<c+k)
				return (ll)(maxv[now]-c)*num[now];
			if(minv[now]>=c+k)
				return sumv[now]-(ll)c*(r-l+1);
		}
		int mid=(l+r)>>1;
		ll ans=0;
		if(ql<=mid)
			ans+=query(lson,l,mid);
		if(qr>=mid+1)
			ans+=query(rson,mid+1,r);
		return ans;
	}
}T;

int main()
{
  freopen("forging.in","r",stdin);
  freopen("forging.out","w",stdout);
	int n;
	read(n);
	for(int i=2;i<=n;++i) // edit 1:root has no father
	{
		static int fa,w;
		read(fa);read(w);
		addedge(fa,i,w);
	}
	dfs(1);
	T.build(1,1,n);
	int q;
	read(q);
	while(q--)
	{
		static int u;
		read(u);read(k);
		ql=dfn[u],qr=dfn[u]+siz[u]-1,c=cost[u];
		printf("%lld\n",T.query(1,1,n));
	}
//  fclose(stdin);
//  fclose(stdout);
    return 0;
}

法二:平衡树

题意是给定一棵带边权的有根树,每次询问一个点u的子树内与其距离至少是k的点到u的距离之和。
容易想到如果我们能维护出每个点子树内点到其距离的有序表,询问将十分简单。我们将询问离线挂在每个点上,从下向上用平衡树维护每个点的子树内距离,合并时启发式合并。
\(O(n \log^2 n + q \log n)\)

#include <bits/stdc++.h>

using LL = long long;

const int MAXN = 1e5 + 5;

struct SplayTree {
    struct Node {
        Node *fa, *ch[2];
        LL sum, tag, val;
        int size;
        
        Node(LL _val): sum(_val), val(_val) {
            fa = ch[0] = ch[1] = 0;
            tag = 0;
            size = 1;
        }
        
        void Add(LL w) {
            val += w;
            sum += w * size;
            tag += w;
        }
        
        void Down() {
            for (int i = 0; i < 2; ++i) if (ch[i])
                ch[i]->Add(tag);
            tag = 0;
        }
        
        void DownR() {
            if (fa) fa->DownR();
            Down();
        }
        
        void Update() {
            sum = val;
            size = 1;
            for (int i = 0; i < 2; ++i) if (ch[i]) {
                sum += ch[i]->sum;
                size += ch[i]->size;
            }
        }
        
        int Which() {
            return this == fa->ch[1];
        }
    } *root;
    
    SplayTree() = default;
    
    void Rotate(Node *k) {
        Node *p = k->fa;
        int l = k->Which(), r = l ^ 1;
        
        k->fa = p->fa;
        if (p->fa) p->fa->ch[p->Which()] = k;
        
        p->ch[l] = k->ch[r];
        if (k->ch[r]) k->ch[r]->fa = p;
        
        k->ch[r] = p;
        p->fa = k;
        
        p->Update();
        k->Update();
    }
    
    void Splay(Node *k, Node *aim_fa = 0) {
        k->DownR();
        
        while (k->fa != aim_fa) {
            Node *p = k->fa;
            if (p->fa != aim_fa) {
                if (k->Which() ^ p->Which()) Rotate(k);
                else Rotate(p);
            }
            Rotate(k);
        }
        
        if (!aim_fa) root = k;
    }
    
    void Insert(LL val, Node *&k, Node *fa = 0) {
        if (!k) {
            k = new Node(val);
            k->fa = fa;
            Splay(k);
            return;
        }
        k->Down();
        Insert(val, k->ch[val > k->val], k);
    }
    
    void Add(LL val) {
        root->Add(val);
    }
    
    void Traverse(std::vector<LL> &vec, Node *k) {
        if (!k) return;
        k->Down();
        Traverse(vec, k->ch[0]);
        vec.push_back(k->val);
        Traverse(vec, k->ch[1]);
    }
    
    void Delete(Node *&k) {
        if (!k) return;
        for (int i = 0; i < 2; ++i) Delete(k->ch[i]);
        delete k; k = 0;
    }
    
    void Merge(SplayTree &oth) {
        if (root->size < oth.root->size) std::swap(root, oth.root);
        
        // std::cerr << root->size << " " << oth.root->size << std::endl;
        
        std::vector<LL> list;
        oth.Traverse(list, oth.root);
        oth.Delete(oth.root);
        for (LL i: list)
            Insert(i, root);
    }
    
    LL Query(LL val) {
        Node *k = root, *p = 0;
        while (k) {
            k->Down();
            if (k->val < val) {
                p = k;
                k = k->ch[1];
            } else {
                k = k->ch[0];
            }
        }
        if (!p) return root->sum;
        Splay(p);
        if (!root->ch[1]) {
            return 0;
        } else {
            return root->ch[1]->sum;
        }
    }
} splay[MAXN];

int n, q;
std::vector<int> G[MAXN];
int fa[MAXN], w[MAXN];

std::vector<std::pair<int, int>> qry[MAXN];

LL ans[MAXN];

void DFS(int now) {
    splay[now].Insert(0, splay[now].root);
    for (int to: G[now]) {
        DFS(to);
        
        splay[to].Add(w[to]);
        splay[now].Merge(splay[to]);
    }
    
    for (auto i: qry[now]) {
        int k = i.first, idx = i.second;
        
        ans[idx] = splay[now].Query(k);
    }
}

void Main() {
#ifndef LOCAL
    freopen("forging.in", "r", stdin);
    freopen("forging.out", "w", stdout);
#endif
    
    scanf("%d", &n);
    for (int i = 2; i <= n; ++i) {
        scanf("%d%d", fa + i, w + i);
        G[fa[i]].push_back(i);
    }
    
    scanf("%d", &q);
    for (int i = 1; i <= q; ++i) {
        static int u, k;
        
        scanf("%d%d", &u, &k);
        qry[u].emplace_back(k, i);
    }
    
    DFS(1);
    
    for (int i = 1; i <= q; ++i)
        printf("%lld\n", ans[i]);
}

register char *_sp __asm__("rsp");

int main() {
    const int size = 32 << 20;
    static char *sys, *mine(new char[size] + size - 4096);
    sys = _sp; _sp = mine; Main(); _sp = sys;
    
    return 0;
}

posted on 2018-09-26 17:27  autoint  阅读(347)  评论(0编辑  收藏  举报

导航