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

    表示调这道题已经调到失智了。。。 因为昨天刚写完线段树2,所以pushdown就写得和线段树2一模一样,,,,于是,,,成功的在pushdown的地方,,,各种错

    下面讲做法: 首先我们可以观察到每个骑士都是独立的,因此对于每个城池我们可以建一个堆,堆中维护的是在这个城池的骑士。

    维护一个小根堆,所以如果堆顶的骑士攻击力不够的话,就可以直接pop掉,并且给城池死亡人数统计++(dead),这样用while pop完后,堆里的骑士就都是可以占领这个城池的了,

    由于下面的骑士可以上来,所以这个堆要支持合并,所以我们用左偏树

    因为pop完后剩下的都是合法的,所以这个时候我们就可以对剩下的骑士进行修改了。但是我们观察到,骑士很多,城池也很多,直接修改显然不太妥当。于是我们借鉴线段树的lazy标记,但是由于这里有乘法也有加法,所以我们维护两个标记,一个mul(乘法标记),一个lazy(加法标记),

    由于(x+h)q=xq+hq,所以我们可以把h当做lazy,x当做当前值,q是mul,所以我们更改和下传乘法标记的时候,要同时把lazy也=mul,

    这时lazy==hq,mul==q,因此我们要得到xq+h*q这个结果,我们需要先乘后加

    deadin[i]代表骑士i死在哪里了,dead代表死在这个城池的人数,keep是城市的生命值,其他数组应该都好懂了

    因为我们观察到如果还要下传一个标记won来标记骑士又占领了一个城池的话显然是不划算的,因为mul和lazy之所以有必要就是因为过程中要用到,而won标记仅仅是最后要用而已,并且由于骑士的前进路径是一条链,所以我们直接用死亡城市深度-出发城市深度就可以得到攻占几座城了,如果没死的话死亡城市就是0,因此为了维护死亡城市-出发城市这个式子的正确性,我们的deep统计从1开始

    如果还有不懂的就看代码吧,还是有少量注释的

    推荐写之前先写线段树2(luogu3373),有利于理解如何同时维护乘法和加法标记,这里的标记本质上是一样的,但是注意不要像我一样,写完线段树2,pushdown就全写成线段树版本的,,,,于是就各种错,,,

    下面的输出格式是为了方便我调试改的,所以。。。。

    写的可能比较复杂???

    表示并不知道为什么我的代码一直比别人长。。。。

    -------2018.10.11-------优化了代码格式

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define R register int
      4 #define AC 300050
      5 #define LL long long
      6 #define D printf("line in %d\n",__LINE__);
      7 int n, m;
      8 int date[AC], Next[AC], Head[AC], tot, cnt;//前向星
      9 LL v[AC], keep[AC];
     10 int deep[AC], a[AC], dead[AC], deadin[AC];//城池属性, 答案
     11 int b[AC], root[AC];
     12 
     13 inline LL read()
     14 {
     15     LL x = 0; char c = getchar(); bool z = false;
     16     while(c > '9' || c < '0') 
     17     {
     18         if(c == '-') z = true;
     19         c = getchar();
     20     }
     21     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
     22     if(!z) return x;
     23     else return -x;
     24 }
     25 
     26 inline void add(int f, int w)
     27 {
     28     date[++tot] = w, Next[tot] = Head[f], Head[f] = tot;
     29 }
     30 
     31 struct left_heap
     32 {
     33     LL s[AC], mul[AC], lazy[AC];//骑士属性和标记,error!!!标记也要LL啊啊啊啊啊啊啊啊,因为标记也可能乘到很大啊,比如lazy和mul各种乘之类的
     34     int l[AC], r[AC], num[AC];
     35 
     36     inline void pushdown(int x)
     37     {
     38         int ll = l[x], rr = r[x];//error!!!这又不是线段树啊!!!!干嘛乘2啊 
     39         if(!mul[x]) return ;//如果mul为0,说明没有这个堆,所以
     40         if(mul[x] != 1)
     41         {
     42             s[ll] *= mul[x], mul[ll] *= mul[x], lazy[ll] *= mul[x];//error!!!加法标记也要乘啊!!!
     43             s[rr] *= mul[x], mul[rr] *= mul[x], lazy[rr] *= mul[x];
     44             mul[x] = 1;
     45         }
     46         if(lazy[x])
     47         {
     48             s[ll] += lazy[x], lazy[ll] += lazy[x];
     49             s[rr] += lazy[x], lazy[rr] += lazy[x];
     50             lazy[x]=0;//error!!!不是线段树啊,,,,r,l不是区间了
     51         }
     52     }
     53 
     54     inline void change_add(int x, int k)
     55     {
     56         if(!x) return ;
     57         s[x] += k, lazy[x] += k;
     58     }
     59 
     60     inline void change_mul(int x, int k)
     61     {
     62         if(!x) return ;
     63         s[x] *= k, lazy[x]*=k, mul[x]*=k;
     64     }
     65 
     66     int merge(int x, int y)
     67     {
     68         pushdown(x), pushdown(y);
     69         if(!x || !y) return x + y;
     70     //  pushdown(x),pushdown(y);
     71         if(s[x] > s[y]) swap(x, y);
     72         r[x] = merge(r[x], y);
     73         swap(l[x], r[x]);
     74         return x;
     75     }
     76 
     77     void pop(int &x)
     78     {
     79         x=merge(l[x], r[x]);
     80     }
     81 
     82     inline void insert(int x, int k)
     83     {
     84         s[++cnt] = x, num[cnt] = k, mul[cnt] = 1;
     85         root[b[k]] = merge(cnt, root[b[k]]);
     86     }
     87 }heap;
     88 
     89 void DFS(int x)
     90 {
     91 /*  if(a[x]) heap.change_mul(root[x],v[x]);
     92     else heap.change_add(root[x],v[x]);
     93     if(!Head[x]) return ;//如果到叶节点就返回,因为没有儿子给它了
     94     */
     95     R now;
     96     for(R i = Head[x]; i; i = Next[i])
     97     {
     98 
     99         now = date[i], deep[now] = deep[x] + 1;
    100         DFS(now);
    101         root[x] = heap.merge(root[now], root[x]);
    102     }
    103     while(root[x] && heap.s[root[x]] < keep[x])
    104     {
    105         dead[x] ++;
    106         deadin[heap.num[root[x]]] = x;
    107         heap.pop(root[x]);
    108     }
    109     if(a[x]) heap.change_mul(root[x], v[x]);
    110     else heap.change_add(root[x], v[x]);
    111 }
    112 
    113 void pre()
    114 {
    115     int aa;
    116     n = read(), m = read();
    117     deep[1] = 1;
    118     for(R i = 1; i <= n; i ++) keep[i] = read(), heap.mul[i] = 1;
    119     for(R i = 2; i <= n; i ++)
    120     {
    121         aa = read(), a[i] = read(), v[i] = read();
    122         add(aa, i);
    123     }
    124     for(R i = 1; i <= m; i ++)
    125     {
    126         aa = read(), b[i] = read();
    127     //  printf("%d %d\n",aa,b[i]);
    128         if(aa >= keep[b[i]])//如果第一座城池打得下才建堆
    129         {
    130             if(!root[b[i]])
    131             {
    132                 root[b[i]] = ++cnt;//应该要给城池开堆,堆里存士兵,不然士兵在哪个城市有上面区别
    133                 heap.num[cnt] = i, heap.s[cnt] = aa, heap.mul[cnt] = 1;//因为是给城池开的堆,所以就要存编号了
    134                 //printf("%d\n",i);
    135             }
    136             else heap.insert(aa, i);
    137         }
    138         else dead[b[i]] ++, deadin[i] = b[i];//不然就别来了
    139     }
    140 }
    141 
    142 void work()
    143 {
    144     for(R i = 1; i <= n; i ++) printf("%d\n", dead[i]);
    145     printf("\n");
    146     for(R i = 1; i <= m; i ++)
    147         printf("%d\n", deep[b[i]] - deep[deadin[i]]);
    148 }
    149 
    150 int main()
    151 {
    152     freopen("in.in","r",stdin);
    153     pre();
    154     DFS(1);
    155     work();
    156     fclose(stdin);
    157     return 0;
    158 }
    View Code
    本文不允许商业性使用,个人转载请注明出处! 知识共享许可协议
    本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。
  • 相关阅读:
    TCP三次握手过程
    btree b+tree 的关系
    volatile和指令重排序
    事务一致性理解 事务ACID特性的完全解答
    JVM 详解
    java 并发 详解
    socker TCP UDP BIO NIO
    mysql 主从复制 配置
    身份证格式验证 方法
    分布式事务 XA 两段式事务 X/open CAP BASE 一次分清
  • 原文地址:https://www.cnblogs.com/ww3113306/p/8759040.html
Copyright © 2011-2022 走看看