• 树上的好题


    T1.[LNOI2014]LCA

    给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。
    设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
    有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)]。
    (即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)

     

    这道题一上来乍眼一看没什么思路,其实之后的每步都是套路;

    对于LCA,我们首先会想到差分,没错,就是差分;

    咋查分啊?一个区间的点啊,这复杂度可不是闹着玩的;

    但有没有注意到,z只有一个;我们是否可以把整个区间在树上差分,然后用这一个点z来查询;

    答案是当然可以:(注意:这里的差分与普通的树形差分不一样)

    我们把z到root这条路上的所有点都打上标记(点的权值加1);那么你会发现,这条路径上任意一点的深度就是到根节点的点权和;

    所以询问区间[L,R],我们只要询问每个点到根节点的点权和就可以了;

    另外,这个结论存在一个优美的性质:若将这个区间的所有点到根节点路径的点权值加1,那么询问时可以找z到根节点的权值和;

    通过以上的的操作,我们将闹着玩的复杂度降到了似乎可做的复杂度;

    接下来我们接着优化:
    这么多区间的查询,是否想到了数位dp的经典处理方法?
    没错,这是一个前缀和优化处理;

    我们将询问离线掉;然后从1到n加入这个点;并将这个点到根节点的路径上所有点的权值加1;

    然后复杂度变成了看起来好好做的样子;

    在思维想不动的时候,就换种解题思路,使用数据结构大力维护每个点到根节点的点权和;

    你想到了什么?树剖?LCT?没问题!

    接下来就是愉快的AC时间了;

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define ll long long
    #define inf 1000000000
    #define mod 201314
    using namespace std;
    inline ll read()
    {
        ll x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    int n,m,cnt,place;
    int bin[20];
    int son[50005],last[50005],fa[50005],belong[50005],pl[50005],deep[50005];
    struct edge{int to,next;}e[50005];
    struct que{int z,ans1,ans2;}q[50005];
    struct data{int num,p;bool flag;}a[100005];
    struct seg{int l,r,sum,tag,size;}t[200005];
    inline bool operator<(data a,data b)
    {
    	return a.p<b.p;
    }
    inline void insert(int u,int v)
    {
    	e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
    }
    void dfs1(int x)
    {
    	son[x]=1;
    	for(int i=last[x];i;i=e[i].next)
    	{
    		if(e[i].to==fa[x])continue;
    		deep[e[i].to]=deep[x]+1;
    		fa[e[i].to]=x;
    		dfs1(e[i].to);
    		son[x]+=son[e[i].to];
    	}
    }
    void dfs2(int x,int chain)
    {
    	belong[x]=chain;pl[x]=++place;
    	int k=n;
    	for(int i=last[x];i;i=e[i].next)
    		if(e[i].to!=fa[x]&&son[e[i].to]>son[k])
    			k=e[i].to;
    	if(k!=n)dfs2(k,chain);
    	for(int i=last[x];i;i=e[i].next)
    		if(e[i].to!=fa[x]&&e[i].to!=k)
    			dfs2(e[i].to,e[i].to);
    }
    inline void pushdown(int k)
    {
    	if(t[k].l==t[k].r||!t[k].tag)return;
    	int tag=t[k].tag;t[k].tag=0;
    	t[k<<1].sum=t[k<<1].sum+t[k<<1].size*tag;
    	t[k<<1|1].sum=t[k<<1|1].sum+t[k<<1|1].size*tag;
    	t[k<<1].tag=t[k<<1].tag+tag;
    	t[k<<1|1].tag=t[k<<1|1].tag+tag;
    }
    void build(int k,int l,int r)
    {
    	t[k].l=l;t[k].r=r;t[k].size=r-l+1;
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	build(k<<1,l,mid);
    	build(k<<1|1,mid+1,r);
    }
    void update(int k,int x,int y)
    {
    	pushdown(k);
    	int l=t[k].l,r=t[k].r;
    	if(l==x&&y==r)
    	{
    		t[k].tag++;t[k].sum+=t[k].size;
    		return;
    	}
    	int mid=(l+r)>>1;
    	if(y<=mid)update(k<<1,x,y);
    	else if(x>mid)update(k<<1|1,x,y);
    	else 
    	{
    		update(k<<1,x,mid);
    		update(k<<1|1,mid+1,y);
    	}
    	t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
    }
    void solve_update(int x,int f)
    {
    	while(belong[x]!=belong[f])
    	{
    		update(1,pl[belong[x]],pl[x]);
    		x=fa[belong[x]];
    	}
    	update(1,pl[f],pl[x]);
    }
    int query(int k,int x,int y)
    {
    	pushdown(k);
    	int l=t[k].l,r=t[k].r;
    	if(l==x&&y==r)return t[k].sum;
    	int mid=(l+r)>>1;
    	if(y<=mid)return query(k<<1,x,y);
    	else if(x>mid)return query(k<<1|1,x,y);
    	else return query(k<<1,x,mid)+query(k<<1|1,mid+1,y);
    }
    int solve_query(int x,int f)
    {
    	int sum=0;
    	while(belong[x]!=belong[f])
    	{
    		sum+=query(1,pl[belong[x]],pl[x]);
    		sum%=mod;
    		x=fa[belong[x]];
    	}
    	sum+=query(1,pl[f],pl[x]);sum%=mod;
    	return sum;
    }
    int main()
    {
    	//freopen("lca.in","r",stdin);
    	//freopen("lca.out","w",stdout);
    	bin[0]=1;for(int i=1;i<20;i++)bin[i]=(bin[i-1]<<1);
        n=read();m=read();
    	for(int i=1;i<n;i++)
    	{
    		int x=read();insert(x,i);
    	}
    	int tot=0;
    	for(int i=1;i<=m;i++)
    	{
    		int l=read(),r=read();
    	    q[i].z=read();
    		a[++tot].p=l-1;a[tot].num=i;a[tot].flag=0;
    	    a[++tot].p=r;a[tot].num=i;a[tot].flag=1;
    	}
    	build(1,1,n);
    	sort(a+1,a+tot+1);
    	dfs1(0);dfs2(0,0);
    	int now=-1;
    	for(int i=1;i<=tot;i++)
    	{
    		while(now<a[i].p)
    		{
    			now++;
    			solve_update(now,0);
    		}
    		int t=a[i].num;
    	    if(!a[i].flag)q[t].ans1=solve_query(q[t].z,0);
    		else q[t].ans2=solve_query(q[t].z,0);
    	}
    	for(int i=1;i<=m;i++)
    		printf("%d
    ",(q[i].ans2-q[i].ans1+mod)%mod);
    	return 0;
    }
    

     T2.我将之命名为树上背包

    题意:给你一颗树,一共n个点,每个点有两种形态:A/B,其中A形态的点有m个,但你不知道具体是哪m个;

      显然的,B类点有n-m个;

      如果任意两个点的类别相同,那么就会对答案做出这两个点之间的距离的贡献;

            问答案最大是多少;

    这道题主要是运用到了背包的思想;

    我们做树上的题是要尽量避免枚举任意两个点的思想,要不然再怎么想基本还是暴力;

    所以我们枚举LCA(比较常见的操作)

    对于作为LCA的点u,设f[u][j]表示在以u为根的字树种,选择j个A类点的价值最大是多少;

    那么在转移时,我们枚举以u为根的子树已经选择的A类点的数量j,和在目前的这个以v为根节点的子树中选择A类点的个数k;

    显然的,这条边会被A类点对经过(k*(m-k)),会被B类点经过(size[v]-k)*(n-m-size[v]+k);

    那么这条边的对答案的贡献值就显而易见了~

    #include <bits/stdc++.h>
    #define inc(i,a,b) for(register int i=a;i<=b;i++)
    #define dec(i,a,b) for(register int i=a;i>=b;i--)
    using namespace std;
    int head[200010],cnt;
    class littlestar{
        public:
        int to,nxt;
        long long w;
        void add(int u,int v,long long gg){
            to=v; nxt=head[u];
            head[u]=cnt; w=gg;
        }
    }star[400010];
    int n,m;
    long long f[3010][3010];
    long long size[200010];
    void dfs(register int u,register int fa)
    {
        size[u]=1;
        for(int i=head[u];i;i=star[i].nxt){
            int v=star[i].to;
            if(v==fa) continue;
            dfs(v,u);            
            dec(j,min(m,(int)size[u]),0){
                dec(k,min(m-j,(int)size[v]),0){
                    f[u][j+k]=max(f[u][j+k],f[u][j]+f[v][k]+(long long)k*(m-k)*star[i].w+(long long)(n-size[v]-m+k)*(size[v]-k)*star[i].w);
                }
            }
            size[u]+=size[v];            
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        inc(i,1,n-1){
            int u,v;
            long long w;
            scanf("%d%d%lld",&u,&v,&w);
            star[++cnt].add(u,v,w);
            star[++cnt].add(v,u,w);
        }
        dfs(1,0);
        cout<<f[1][m];
    }
    /*
    5 4
    1 2 1
    2 3 3
    2 4 1
    1 5 3
    */
  • 相关阅读:
    RobotFramework+Selenium2+Appium环境搭建
    spring mvc 依赖包
    linux
    清理linux 某个文件夹下面所有的log文件
    selenium grid2 使用远程机器的浏览器
    IntelliJ Idea 快捷键
    aop注解 自定义切面的注解写法
    springmvc多视图配置
    @Autowired(required = false)
    pom.xml配置详解
  • 原文地址:https://www.cnblogs.com/kamimxr/p/11801084.html
Copyright © 2020-2023  润新知