hdu4918 (树分治的维护)

题意:

一棵树,每个点有点权w,q次操作

1.将点x的点权修改为y 

2.问离x距离不超过d的点权和

 

思路:

将树分治中的计算部分需要的树状数组提出来,维护这个树状数组,每次查询修改均为O(log2n)

 

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
vector<int>link[N];
struct MSG
{
    int id1,id2;
    int deep;
}msg[N][17];
int w[N];
int c[N*17];
int idn,L[N*2],R[N*2];
int maxfloor[N];
void add(int l,int r,int x,int y)
{
    for (int i=x;i<r-l;i+=(i&-i)) c[l+i]+=y;
}
int get(int l,int r,int x)
{
    if (x<1) return 0;
    if (x>=r-l) x=r-l-1;
    int ret=0;
    for (int i=x;i;i-=(i&-i)) ret+=c[l+i];
    return ret;
}
bool vis[N];
int cent,mxsz;
int size[N];
void findcent(int u,int fa,int n)
{
	size[u]=1;
	int mx=0;
	for (int v:link[u])
	{
		if (vis[v]||v==fa) continue;
		findcent(v,u,n);
		size[u]+=size[v];
		mx=max(mx,size[v]);
	}
	mx=max(mx,n-size[u]);
	if (mx<mxsz) cent=u,mxsz=mx;
}
int getmaxdeep(int u,int fa)
{
    int ret=1;
    for (int v:link[u])
        if (v!=fa&&!vis[v]) ret=max(ret,1+getmaxdeep(v,u));
    return ret;
}
void dfs(int u,int fa,int deep,int idn,int floor,int tp)
{
    if (tp==0) msg[u][floor].id1=idn;
    else msg[u][floor].id2=idn;
    msg[u][floor].deep=deep;
    add(L[idn],R[idn],deep,w[u]);
    for (int v:link[u])
        if (!vis[v]&&v!=fa) dfs(v,u,deep+1,idn,floor,tp);
}
void build(int u,int n,int floor)
{
    cent=u; mxsz=n;
    findcent(u,-1,n);
    u=cent; maxfloor[u]=floor;
    idn++; L[idn]=R[idn-1];
    R[idn]=L[idn]+getmaxdeep(u,-1)+1;

    dfs(u,-1,1,idn,floor,0);

    msg[u][floor].id2=-1;
    for (int v:link[u])
        if (!vis[v])
        {
            idn++; L[idn]=R[idn-1];
            R[idn]=L[idn]+getmaxdeep(v,u)+2;
            dfs(v,u,2,idn,floor,1);
        }

    vis[u]=1;
    for (int v:link[u])
        if (!vis[v])
            if (size[v]<size[u]) build(v,size[v],floor+1);
            else build(v,n-size[u],floor+1);

}
int main()
{
    int n,q;
    while(~scanf("%d%d",&n,&q))
    {
        for (int i=1;i<=n;i++) scanf("%d",&w[i]);
        for (int i=1;i<n;i++)
        {
            int x,y; scanf("%d%d",&x,&y);
            link[x].push_back(y);
            link[y].push_back(x);
        }
        build(1,n,0);
        while(q--)
        {
            char s[2]; int x,y;
            scanf("%s%d%d",s,&x,&y);
            if (s[0]=='?')
            {
                int ans=0;
                for (int floor=0;floor<=maxfloor[x];floor++)
                {
                    int id1=msg[x][floor].id1;
                    int id2=msg[x][floor].id2;
                    int deep=msg[x][floor].deep;
                    ans+=get(L[id1],R[id1],y+2-deep);
                    if (id2!=-1) ans-=get(L[id2],R[id2],y+2-deep);
                }
                printf("%d\n",ans);
            }
            else
            {
                for (int floor=0;floor<=maxfloor[x];floor++)
                {
                    int id1=msg[x][floor].id1;
                    int id2=msg[x][floor].id2;
                    int deep=msg[x][floor].deep;
                    add(L[id1],R[id1],deep,y-w[x]);
                    if (id2!=-1) add(L[id2],R[id2],deep,y-w[x]);
                }
                w[x]=y;
            }
        }
        memset(vis,0,sizeof(vis));
        memset(msg,0,sizeof(msg));
        memset(w,0,sizeof(w));
        memset(c,0,sizeof(c));
        idn=0;
        for (int i=1;i<=n;i++) link[i].clear();
    }
}

NOI2014购票(树的cdq分治+凸壳)

题目链接:

http://uoj.ac/problem/7

 

思路:

如果没有距离限制L的话只要dfs一遍同时维护到根的凸壳就可以(二分位置后可以做到O(1)添加,O(1)恢复)。

这题有个距离限制的话就可以将重心下面的点按d[v]-l[v]排序后,与重心到根的点一起双指针更新。

 

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=200005;
struct E
{
	int v; ll z;
};
vector<E>link[N];
ll d[N],p[N],q[N],l[N],f[N];
int fa[N];

struct P
{
	ll d,f;
	ll operator*(const P &b)const
	{
		return d*b.d+f*b.f;
	}
}stk[N];
int top;
long double slope(P a,P b)
{
    long double tmp=1.0;
    return tmp*(b.f-a.f)/(b.d-a.d);
}
void add(P p)
{
	if (top<=1)
	{
		stk[top++]=p;
		return;
	}
	while(top>=2&&slope(p,stk[top-1])>slope(stk[top-1],stk[top-2])) top--;
	stk[top++]=p;
}
void upd(int id)
{
	P vec={-p[id],1};
	int l=0,r=top-1;
	while(l<r)
	{
		int mid=(l+r)/2;
		if (vec*stk[mid]>vec*stk[mid+1]) l=mid+1;
		else r=mid;
	}
	f[id]=min(f[id],p[id]*d[id]+q[id]+vec*stk[l]);
}

int cent,mxsz;
int size[N];
int vis[N];
void findcent(int u,int n)
{
	size[u]=1;
	int mx=0;
	for (E e:link[u])
	{
		int v=e.v;
		if (vis[v]) continue;
		d[v]=d[u]+e.z;
		findcent(v,n);
		size[u]+=size[v];
		mx=max(mx,size[v]);
	}
	mx=max(mx,n-size[u]);
	if (mx<=mxsz) cent=u,mxsz=mx;
}

int nd[N],tot;
void add(int u)
{
	nd[tot++]=u;
	for (E e:link[u])
		if (!vis[e.v]) add(e.v);
}
bool cmp(int i,int j)
{
	return d[i]-l[i]>d[j]-l[j];
}
void solve(int u,int n)
{
	if (n==1) return;
	cent=u;  mxsz=n;
	findcent(u,n);
	int rt=cent;
	for (E e:link[rt]) vis[e.v]++;
	if (u==4&&n==2)
		u=4;
	solve(u,n-size[rt]+1);

	for (E e:link[rt]) vis[e.v]--;
	for (E e:link[rt]) add(e.v);
	sort(nd,nd+tot,cmp);
	int now=rt;
	for (int i=0;i<tot;i++)
	{
		int id=nd[i];
		while(now!=fa[u]&&d[now]>=d[id]-l[id]) add({d[now],f[now]}),now=fa[now];
		if (top) upd(id);
	}
	tot=0; top=0;
	for (E e:link[rt]) solve(e.v,size[e.v]);
}
int main()
{
	int n,t; scanf("%d%d",&n,&t);
	for (int i=2;i<=n;i++)
	{
		f[i]=1ll<<62;
		ll s; scanf("%d%lld",&fa[i],&s);
		link[fa[i]].push_back({i,s});
		scanf("%lld%lld%lld",&p[i],&q[i],&l[i]);
	}
	solve(1,n);
	for (int i=2;i<=n;i++) printf("%lld\n",f[i]);
}