[JLOI2015]城池攻占 可并堆

题目传送门

难得遇到一个不卡常的,,

维护一个小根堆,堆里是当前有的骑士,按dfs序,所有不能攻占该城市的都弹出,城市阻挡人数++,这个人攻占的城市=depth[出生城市]-depth[该城市];然后把可以攻占的加标记和乘标记更新后再向上合并到它的父节点。所以,我们需要一个支持合并的小根堆。左偏堆板子一套,美滋滋。然后,成功把根攻占的骑士攻占城市数=depth[出生城市]。然后直接输出即可。

最多m次合并,每次最多带n个人,O(mn);初始插入O(mlogm),每个人最多弹1次,O(mlogm)。综上,就是O(mn+mlogm)。

代码,,一如既往地丑,,

#include<bits/stdc++.h>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<deque>
#include<list>
#include<set>
#include<vector>
#include<iostream>
#define ll long long
#define re register
#define inf 0x7f7f7ff
#define inl inline
#define sqr(x) (x*x)
//#define eps 1e-8
#define debug printf("debug\n");
//#pragma comment(linker, "/STACK:1024000000,1024000000")
//#pragma GCC optimize (2)
//#pragma G++ optimize (2)
using namespace std;
//const ll mod;
const ll MAXN=3e5+10;
inl ll read() {
    re ll x = 0; re int f = 1;
    char ch = getchar();
    while(ch<'0'||ch>'9') { if(ch== '-' ) f = -1; ch = getchar(); }
    while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x * f;
}
inl char readc() {
    char ch=getchar();
    while(('z'<ch||ch<'a')&&('Z'<ch||ch<'A')) ch=getchar();
    return ch;
}
inl void write(re ll x){
    if(x>=10)write(x/10);
    putchar(x%10+'0');
}
inl void writeln(re ll x){
    if(x<0) {x=-x;putchar('-');}
    write(x); puts("");
}
inl ll gcd(re ll x,re ll y){while(y^=x^=y^=x%=y);return x;}
inl ll Lcm(re ll a,re ll b) {return a/gcd(a,b)*b;}
inl void FR() {
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
}
inl void FC() {
    fclose(stdin);
    fclose(stdout);
}
struct Node {
    ll ls,rs,dis,val;
    ll add,mul;
}Tree[MAXN];
inl void cov(ll x,ll add,ll mul) {
    Tree[x].val*=mul;Tree[x].val+=add;
    Tree[x].add*=mul;Tree[x].mul*=mul;Tree[x].add+=add;
}
inl void pushdown(ll x) {
    cov(Tree[x].ls,Tree[x].add,Tree[x].mul);
    cov(Tree[x].rs,Tree[x].add,Tree[x].mul);
    Tree[x].add=0;Tree[x].mul=1;
}
ll merge(ll a,ll b) {
    if(!a||!b) return a^b;
    pushdown(a),pushdown(b);
    if(Tree[a].val>Tree[b].val) swap(a,b);
    Tree[a].rs=merge(Tree[a].rs,b);
    if(Tree[Tree[a].ls].dis<Tree[Tree[a].rs].dis) swap(Tree[a].ls,Tree[a].rs);
    Tree[a].dis=Tree[Tree[a].rs].dis+1;
    return a;
}
ll n,m,cnt,head[MAXN],h[MAXN],depth[MAXN];
ll rt[MAXN],a[MAXN],v[MAXN],c[MAXN],ans[2][MAXN];
struct edge {
    ll u,v,nxt;
}e[MAXN];
inl void adde(ll u,ll v) {
    e[++cnt].u=u;e[cnt].v=v;
    e[cnt].nxt=head[u];head[u]=cnt;
}
void dfs(ll x,ll fa) {
    for(re ll hs=head[x];hs;hs=e[hs].nxt) {
        re ll sv=e[hs].v;
        depth[sv]=depth[x]+1;
        dfs(sv,x);rt[x]=merge(rt[x],rt[sv]);
    }
    while(rt[x]&&Tree[rt[x]].val<h[x]) {
        pushdown(rt[x]);
        ++ans[0][x];ans[1][rt[x]]=depth[c[rt[x]]]-depth[x];
        rt[x]=merge(Tree[rt[x]].ls,Tree[rt[x]].rs);
    }
    if(a[x]) cov(rt[x],0,v[x]);
    else cov(rt[x],v[x],1);
}
int main() {
//  FR();
      n=read(),m=read();
      for(re ll i=1;i<=n;i++) h[i]=read();
      for(re ll i=2;i<=n;i++) {
          re ll f=read();adde(f,i);
          a[i]=read(),v[i]=read();
    }
    for(re ll i=1;i<=m;i++) {
        Tree[i].val=read(),c[i]=read();
        Tree[i].mul=1;rt[c[i]]=merge(rt[c[i]],i);
    }
    depth[1]=1;dfs(1,0);
    while(rt[1]) {
        pushdown(rt[1]);ans[1][rt[1]]=depth[c[rt[1]]];
        rt[1]=merge(Tree[rt[1]].ls,Tree[rt[1]].rs);
    }
    for(re ll i=1;i<=n;i++) writeln(ans[0][i]);
    for(re ll i=1;i<=m;i++) writeln(ans[1][i]);
//  FC();
    return 0;
}

 

转载于:https://www.cnblogs.com/20020723YJX/p/9470835.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值