zoukankan      html  css  js  c++  java
  • @bzoj


    @description@

    n 个城池构成一棵有根树,第 i 个城池的父亲为 fi(fi < i),防御值为 hi。

    有 m 个骑士,第 i 个骑士的初始战斗力为 si,第一个攻击的城池为 ci。

    如果一个骑士的战斗力大于等于城池的生命值,那么骑士就可以占领这座城池;否则骑士将在这座城池牺牲。

    占领一个城池以后,骑士的战斗力将发生变化,然后继续攻击这座城池的父亲,直到占领 1 号城池或牺牲为止。

    除 1 号城池外,每个城池 i 会给出一个战斗力变化参数 ai,vi。若 ai =0,攻占城池 i 以后骑士战斗力会增加 vi;若 ai =1,攻占城池 i 以后,战斗力会乘以 vi,这个时候保证 vi > 0。

    骑士之间不会互相影响的。

    问对于每个城池,有多少个骑士在这里牺牲;对于每个骑士,他攻占的城池数量。

    input
    第 1 行包含两个正整数 n,m,表示城池的数量和骑士的数量。

    第 2 行包含 n 个整数,其中第 i 个数为 hi,表示城池 i 的防御值。

    第 3 到 n +1 行,每行包含三个整数。其中第 i +1 行的三个数为 fi,ai,vi,分别表示这座城池的父亲和两个战斗力变化参数。

    第 n +2 到 n + m +1 行,每行包含两个整数。其中第 n + i 行的两个数为 si;ci,分别表示初始战斗力和第一个攻击的城池。

    保证 1 <= n;m <= 300000, 1 <= fi<i; 1 <= ci <= n; -10^18 <= hi,vi,si <= 10^18;ai等于1或者2。且当 ai =1 时,vi > 0。

    保证任何时候骑士战斗力值的绝对值不超过 10^18。

    output
    输出 n + m 行,每行包含一个非负整数。其中前 n 行分别表示在城池 1 到 n 牺牲的骑士数量,后 m 行分别表示骑士 1 到 m 攻占的城池数量。

    sample input
    5 5
    50 20 10 10 30
    1 1 2
    2 0 5
    2 0 -10
    1 0 10
    20 2
    10 3
    40 4
    20 4
    35 5
    sample output
    2
    2
    0
    0
    0
    1
    1
    3
    1
    1

    @solution@

    首先题目这个输出格式很显然对在线算法不友好,并且这道题在线也不好做。我们考虑离线。

    我们要离线处理什么呢?处理出每一个骑士在哪个点 die,这样题目中的两个输出要求都可以搞出来了。

    离线有什么好处呢?对于某一个点,我们可以将这个点内所有的骑士一起处理。我们要判断哪些骑士会 die,哪些骑士进入它的父亲。
    可以发现每个骑士只会 die 一次,因此我们直接暴力找到一个会 die 的骑士,然后把它弹走,再重复这个过程就可以了。

    谁最容易 die 呢?自然是攻击力最小的那个。我们对于每一个结点建一个小根堆,弹出攻击力所有不符合要求的骑士,再将这个堆往上送给父亲。
    父亲方面要将所有儿子的堆合并起来,所以使用左偏树。

    还有一个问题,骑士的攻击力要发生改变。我们给堆顶打一个 tag,然后合并的时候将 tag 传下去就可以了。
    因为加上一个数不会影响两个数的大小关系,乘上一个正数也不会影响两个数的大小关系。

    @accepted code@

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXN = 300000;
    struct edge{
    	edge *nxt; int to;
    }edges[MAXN + 5], *adj[MAXN + 5], *ecnt=&edges[0];
    void addedge(int u, int v) {
    	edge *p = (++ecnt);
    	p->to = v, p->nxt = adj[u], adj[u] = p;
    }
    struct node{
    	node *ch[2];
    	int dis, num;
    	ll key, atag, mtag;
    }pl[MAXN + 5], *rt[MAXN + 5], *NIL, *tcnt;
    void pushdown(node *x) {
    	if( x->mtag != 1 ) {
    		if( x->ch[0] != NIL )
    			x->ch[0]->atag *= x->mtag, x->ch[0]->mtag *= x->mtag, x->ch[0]->key *= x->mtag;
    		if( x->ch[1] != NIL )
    			x->ch[1]->atag *= x->mtag, x->ch[1]->mtag *= x->mtag, x->ch[1]->key *= x->mtag;
    		x->mtag = 1;
    	}
    	if( x->atag ) {
    		if( x->ch[0] != NIL )
    			x->ch[0]->atag += x->atag, x->ch[0]->key += x->atag;
    		if( x->ch[1] != NIL )
    			x->ch[1]->atag += x->atag, x->ch[1]->key += x->atag;
    		x->atag = 0;
    	}
    }
    node *merge(node *a, node *b) {
    	if( a == NIL ) return b;
    	if( b == NIL ) return a;
    	if( a->key > b->key ) swap(a, b);
    	pushdown(a);
    	a->ch[1] = merge(a->ch[1], b);
    	if( a->ch[0]->dis < a->ch[1]->dis ) swap(a->ch[0], a->ch[1]);
    	a->dis = a->ch[1]->dis + 1;
    	return a;
    }
    node *newnode(ll x, int n) {
    	tcnt++;
    	tcnt->ch[0] = tcnt->ch[1] = NIL;
    	tcnt->key = x, tcnt->num = n;
    	tcnt->dis = 1, tcnt->atag = 0, tcnt->mtag = 1;
    	return tcnt;
    }
    int dep[MAXN + 5], dfn[MAXN + 5], dcnt;
    void init() {
    	tcnt = NIL = &pl[0], NIL->dis = 0;
    	for(int i=1;i<=MAXN;i++)
    		rt[i] = NIL;
    	dcnt = 0;
    }
    int a[MAXN + 5], dead[MAXN + 5], cnt[MAXN + 5];
    ll h[MAXN + 5], v[MAXN + 5];
    void dfs(int x) {
    	for(edge *p=adj[x];p;p=p->nxt)
    		dep[p->to] = dep[x] + 1, dfs(p->to), rt[x] = merge(rt[x], rt[p->to]);
    	while( rt[x] != NIL && rt[x]->key < h[x] )
    		dead[rt[x]->num] = x, pushdown(rt[x]), rt[x] = merge(rt[x]->ch[0], rt[x]->ch[1]);
    	if( a[x] )
    		rt[x]->atag *= v[x], rt[x]->mtag *= v[x], rt[x]->key *= v[x];
    	else rt[x]->atag += v[x], rt[x]->key += v[x];
    }
    int c[MAXN + 5];
    int main() {
    	int n, m; init();
    	scanf("%d%d", &n, &m);
    	for(int i=1;i<=n;i++)
    		scanf("%lld", &h[i]);
    	for(int i=2;i<=n;i++) {
    		int fa; scanf("%d%d%lld", &fa, &a[i], &v[i]);
    		addedge(fa, i);
    	}
    	for(int i=1;i<=m;i++) {
    		ll s; scanf("%lld%d", &s, &c[i]);
    		rt[c[i]] = merge(rt[c[i]], newnode(s, i));
    	}
    	dep[1] = 1, dfs(1);
    	for(int i=1;i<=m;i++) cnt[dead[i]]++;
    	for(int i=1;i<=n;i++) printf("%d
    ", cnt[i]);
    	for(int i=1;i<=m;i++) printf("%d
    ", dep[c[i]] - dep[dead[i]]);
    }
    

    @details@

    压行好像压的有点儿厉害?希望大家还能看得懂。

    其实并不需要 dfs,因为保证了父亲编号小于当前点的编号,相当于就给出了一个拓扑序。直接在拓扑序上搞就可以了。
    奈何我眼睛比较不好,写博客的时候才突然发现有这样一个条件。

    然后就是注意一下两个标记谁先谁后,显然乘法优先度是高于加法优先度的。

  • 相关阅读:
    vue 自定义全局按键修饰符
    Vue 过滤器
    v-if、v-show 指令
    其他内置函数
    python中序列化和反序列化
    jmeter图形化html报告核心指标介绍
    jmeter在linux系统下如何进行压力测试
    文件操作的其他方法
    文件处理操作
    内置函数reduce()
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10286986.html
Copyright © 2011-2022 走看看