• 【洛谷P3261】城池攻占


    题目

    题目链接:https://www.luogu.com.cn/problem/P3261
    臭不要脸的把 LOJ 的题面截图下来

    思路

    一个显然的思路是从叶子往上跑,将所有可以到点 (x) 的骑士扔进一个小根堆里面,然后不停弹出堆顶直到对顶元素的值不小于 (h[x]) 或者堆空了。
    由于需要堆合并,所以直接上左偏树即可。至于乘和加就在堆顶打 tag,然后合并或弹出的时候再 pushdown 即可。顺序显然是先乘后加。
    对于第一问,答案就是在点 (x) 被弹出的元素个数;对于第二问,答案是该骑士最先攻击的点的深度减去弹出时点的深度。注意对于攻击完点 (1) 依然存活的骑士并不计入 (1) 的答案,且不用减去 (dep[1])
    时间复杂度 (O(nlog m))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const ll N=300010;
    ll n,m,tot,head[N],fa[N],a[N],c[N],rt[N],dep[N],ans1[N],ans2[N],h[N],v[N],s[N];
    
    struct edge
    {
    	ll next,to;
    }e[N];
    
    void add(ll from,ll to)
    {
    	e[++tot]=(edge){head[from],to};
    	head[from]=tot;
    }
    
    struct LeftistTree
    {
    	ll val[N],dis[N],lc[N],rc[N],lazy[N][2];
    	
    	void pushdown(ll x)
    	{
    		if (lazy[x][1]!=1)
    		{
    			val[x]*=lazy[x][1];
    			lazy[lc[x]][1]*=lazy[x][1]; lazy[rc[x]][1]*=lazy[x][1];
    			lazy[lc[x]][0]*=lazy[x][1]; lazy[rc[x]][0]*=lazy[x][1];
    		}
    		if (lazy[x][0])
    		{
    			val[x]+=lazy[x][0];
    			lazy[lc[x]][0]+=lazy[x][0]; lazy[rc[x]][0]+=lazy[x][0];
    		}
    		lazy[x][1]=1; lazy[x][0]=0;
    	}
    	
    	ll merge(ll x,ll y)
    	{
    		if (!x || !y) return x+y;
    		pushdown(x); pushdown(y);
    		if (val[x]>val[y]) swap(x,y);
    		rc[x]=merge(rc[x],y);
    		if (dis[rc[x]]>dis[lc[x]]) swap(lc[x],rc[x]);
    		dis[x]=dis[rc[x]]+1;
    		return x;
    	}
    }lt;
    
    void dfs(ll x)
    {
    	dep[x]=dep[fa[x]]+1;
    	for (ll i=head[x];~i;i=e[i].next)
    	{
    		ll v=e[i].to;
    		dfs(v);
    		rt[x]=lt.merge(rt[x],rt[v]);
    	}
    	lt.pushdown(rt[x]);
    	while (rt[x] && lt.val[rt[x]]<h[x])
    	{
    		ans1[x]++;
    		ans2[rt[x]]=dep[c[rt[x]]]-dep[x];
    		rt[x]=lt.merge(lt.lc[rt[x]],lt.rc[rt[x]]);
    		lt.pushdown(rt[x]);
    	}
    	if (a[x]==0) lt.lazy[rt[x]][0]+=v[x];
    		else lt.lazy[rt[x]][1]*=v[x],lt.lazy[rt[x]][0]*=v[x];
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%lld%lld",&n,&m);
    	for (ll i=1;i<=n;i++)
    		scanf("%lld",&h[i]);
    	for (ll i=2;i<=n;i++)
    	{
    		scanf("%lld%lld",&fa[i],&a[i]);
    		scanf("%lld",&v[i]);
    		add(fa[i],i);
    	}
    	lt.dis[0]=-1;
    	for (ll i=1;i<=m;i++)
    	{
    		scanf("%lld",&s[i]); scanf("%lld",&c[i]);
    		lt.val[i]=s[i]; lt.lazy[i][1]=1;
    		rt[c[i]]=lt.merge(rt[c[i]],i);
    	}
    	dfs(1);
    	lt.pushdown(rt[1]);
    	while (rt[1])
    	{
    		ans2[rt[1]]=dep[c[rt[1]]];
    		rt[1]=lt.merge(lt.lc[rt[1]],lt.rc[rt[1]]);
    		lt.pushdown(rt[1]);
    	}
    	for (ll i=1;i<=n;i++) printf("%lld
    ",ans1[i]);
    	for (ll i=1;i<=m;i++) printf("%lld
    ",ans2[i]);
    	return 0;
    }
    
  • 相关阅读:
    POJ2182Lost Cows
    BZOJ4003: [JLOI2015]城池攻占
    POJ1635Subway tree systems
    BZOJ1005: [HNOI2008]明明的烦恼
    POJ1182 NOI2001 食物链
    栈的链式实现
    栈的数组实现
    链表ADT的实现
    #ifndef的用法
    using namespace std
  • 原文地址:https://www.cnblogs.com/stoorz/p/14071156.html
Copyright © 2020-2023  润新知