zoukankan      html  css  js  c++  java
  • BZOJ4003 [JLOI2015]城池攻占 左偏树 可并堆

    欢迎访问~原文出处——博客园-zhouzhendong

    去博客园看该题解


    题目传送门 - BZOJ4003


    题意概括

    题意有点复杂,直接放原题了。

    小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池。

    这 n 个城池用 1 到 n 的整数表示。除 1 号城池外,城池 i 会受到另一座城池 fi 的管辖,
    其中 fi <i。也就是说,所有城池构成了一棵有根树。这 m 个骑士用 1 到 m 的整数表示,其
    中第 i 个骑士的初始战斗力为 si,第一个攻击的城池为 ci。
    每个城池有一个防御值 hi,如果一个骑士的战斗力大于等于城池的生命值,那么骑士就可
    以占领这座城池;否则占领失败,骑士将在这座城池牺牲。占领一个城池以后,骑士的战斗力
    将发生变化,然后继续攻击管辖这座城池的城池,直到占领 1 号城池,或牺牲为止。
    除 1 号城池外,每个城池 i 会给出一个战斗力变化参数 ai;vi。若 ai =0,攻占城池 i 以后骑士战斗力会增加 vi;若 ai =1,攻占城池 i 以后,战斗力会乘以 vi。注意每个骑士是单独计算的。也就是说一个骑士攻击一座城池,不管结果如何,均不会影响其他骑士攻击这座城池的结果。
    现在的问题是,对于每个城池,输出有多少个骑士在这里牺牲;对于每个骑士,输出他攻占的城池数量。

    题解

      从树根跑dfs。

      对于每一个子树,合并它的所有子树所代表的堆。

      死掉的就弹出就可以了。

      然后修改只需要打两个懒标记就可以了。


    代码

    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <cstdlib>
    #include <cmath>
    #include <vector>
    using namespace std;
    typedef long long LL;
    const int N=300005;
    int n,m;
    vector <int> son[N];
    int c[N],ls[N],rs[N],npl[N],root[N],ans1[N],ans2[N],depth[N];
    LL h[N],op[N],v[N],val[N],add[N],times[N];
    void pushson(int x,LL Add,LL Times){
    	val[x]=val[x]*Times+Add;
    	add[x]=add[x]*Times+Add;
    	times[x]*=Times;
    }
    void pushdown(int x){
    	if (ls[x])
    		pushson(ls[x],add[x],times[x]);
    	if (rs[x])
    		pushson(rs[x],add[x],times[x]);
    	add[x]=0,times[x]=1;
    }
    int merge(int a,int b){
    	if (!a||!b)
    		return a+b;
    	if (val[a]>val[b])
    		swap(a,b);
    	pushdown(a);
    	rs[a]=merge(rs[a],b);
    	if (npl[rs[a]]>npl[ls[a]])
    		swap(rs[a],ls[a]);
    	npl[a]=npl[rs[a]]+1;
    	return a;
    }
    void pop(int &x){
    	pushdown(x);
    	x=merge(ls[x],rs[x]);
    }
    void dfs(int rt){
    	for (int i=0;i<son[rt].size();i++){
    		int s=son[rt][i];
    		depth[s]=depth[rt]+1;
    		dfs(s);
    		root[rt]=merge(root[rt],root[s]);
    	}
    	while (root[rt]&&val[root[rt]]<h[rt]){
    		ans2[root[rt]]=rt;
    		ans1[rt]++;
    		pop(root[rt]);
    	}
    	if (op[rt]==0)
    		pushson(root[rt],v[rt],1);
    	else
    		pushson(root[rt],0,v[rt]);
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++)
    		scanf("%lld",&h[i]);
    	for (int i=1;i<=n;i++)
    		son[i].clear();
    	for (int i=2,fa;i<=n;i++){
    		scanf("%d%lld%lld",&fa,&op[i],&v[i]);
    		son[fa].push_back(i);
    	}
    	memset(root,0,sizeof root);
    	for (int i=1;i<=m;i++){
    		scanf("%lld%d",&val[i],&c[i]);
    		ls[i]=rs[i]=npl[i]=add[i]=0,times[i]=1;
    		root[c[i]]=merge(root[c[i]],i);
    	}
    	memset(ans1,0,sizeof ans1);
    	memset(ans2,0,sizeof ans2);
    	depth[1]=1;
    	dfs(1);
    	for (int i=1;i<=n;i++)
    		printf("%d
    ",ans1[i]);
    	for (int i=1;i<=m;i++)
    		printf("%d
    ",depth[c[i]]-depth[ans2[i]]);
    	return 0;
    }
    

      

  • 相关阅读:
    约瑟夫问题
    LCIS(最长公共上升子序列)
    Spfa求负环
    裴蜀(贝祖)定理
    Tarjan算法之简单缩点
    树形dp-二次扫描+换根
    sys.path.insert
    python学习之路(十二)
    python学习之路(十一)
    python学习之路(十)
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ4003.html
Copyright © 2011-2022 走看看