• [luogu P3787][新创无际夏日公开赛] 冰精冻西瓜 [树状数组][dfs序]


    题目背景

    盛夏,冰之妖精琪露诺发现了一大片西瓜地,终于可以吃到美味的冻西瓜啦。

    题目描述

    琪露诺是拥有操纵冷气程度的能力的妖精,一天她发现了一片西瓜地。这里有n个西瓜,由n-1条西瓜蔓连接,形成一个有根树,琪露诺想要把它们冷冻起来慢慢吃。

    这些西瓜蔓具有神奇的性质,可以将经过它的冷气的寒冷程度放大或缩小,每条西瓜蔓放大/缩小冷气寒冷程度的能力值为Wi,表示冷气经过它后,寒冷程度值x会变为x*wi。每个西瓜也有一个寒冷程度值,炎热的夏日,所有西瓜的寒冷程度值初始都为0。

    琪露诺会做出两种动作:

    ①.对着西瓜i放出寒冷程度为x的冷气。这股冷气顺着西瓜蔓向“西瓜树”的叶子节点蔓延,冷气的寒冷程度会按照上面的规则变化。遇到一个西瓜连了多条西瓜蔓时,每条叶子节点方向的西瓜蔓均会获得与原先寒冷程度相等的冷气。途径的所有西瓜的寒冷程度值都会加上冷气的寒冷程度值。

    ⑨.向你询问西瓜i的寒冷程度值是多少。

    等等,为什么会有⑨?因为笨蛋琪露诺自己也会忘记放了多少冰呢。

    所以,帮她计算的任务就这么交给你啦。

    输入输出格式

    输入格式:

    第一行一个整数n,表示西瓜的数量。

    西瓜编号为1~n,1为这棵“西瓜树”的根。

    接下来n-1行,每行有两个整数u,v和一个实数w,表示西瓜u和西瓜v之间连接有一条藤蔓,它放大/缩小冷气寒冷程度的能力值为w。

    接下来一行一个整数m,表示操作的数量。

    接下来m行,每行两个或三个整数。

    第一个数只能是1或9。

    如果为1,接下来一个整数i和一个实数x,表示对西瓜i放出寒冷程度为x的冷气。

    如果为9,接下来一个整数i,表示询问编号为i的西瓜的寒冷程度值。

    输出格式:

    对于每个操作⑨,输出一行一个实数,表示对应西瓜的寒冷程度值。

    输入输出样例

    输入样例#1:
    4
    1 2 1.00000000
    2 3 0.00000000
    3 4 1.00000101
    9
    1 1 3.00000000
    9 2
    9 3
    1 2 1.42856031
    9 4
    9 2
    1 3 4.23333333
    9 2
    9 4
    输出样例#1:
    3.00000000
    0.00000000
    0.00000000
    4.42856031
    4.42856031
    4.23333761

    说明

    子任务可能出现如下的特殊性质:

    “西瓜树”退化为一条链

    输入数据中的实数均保留8位小数,选手的答案被判作正确当且仅当输出与标准答案误差不超过10^-7。请特别注意浮点数精度问题。

    实际数据中,冷气的寒冷程度x的范围为 [-0.1,0.1]

    (样例中的冷气寒冷程度的范围为[1,5])


    20分代码

    n<=1000  m<=1000

    好小的树

    --->朴素的、完全依照题意的遍历树算法

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<vector>
     5 #include<cmath>
     6 using namespace std;
     7 
     8 inline int read(){
     9     int re=0;
    10     char ch;
    11     bool flag=0;
    12     while((ch=getchar())!='-'&&(ch<'0'||ch>'9'));
    13     ch=='-'?flag=1:re=ch-'0';
    14     while((ch=getchar())>='0'&&ch<='9')  re=(re<<1)+(re<<3)+ch-'0';
    15     return flag?-re:re;
    16 }
    17 
    18 typedef long double lb;
    19 
    20 struct edge{
    21     int to,next;
    22     lb w;
    23     edge(int to=0,int next=0,lb w=0):
    24         to(to),next(next),w(w){}
    25 };
    26 
    27 const int maxn=100001;
    28 
    29 const lb eps=1e-8;
    30 vector<edge> edges;
    31 vector<edge> tree;
    32 int n,m,cnt,root=1;
    33 int head[maxn],tmp_head[maxn];
    34 lb data[maxn];
    35 
    36 bool oo(lb ww){
    37     if(fabs(ww)<eps)  return 0;
    38     return 1;
    39 }
    40 
    41 inline void add_edge(int from,int to,lb w){
    42     edges.push_back(edge(to,head[from],w));
    43     head[from]=++cnt;
    44     edges.push_back(edge(from,head[to],w));
    45     head[to]=++cnt;
    46 }
    47 
    48 inline void add_tree(int from,int to,lb w){
    49     tree.push_back(edge(to,tmp_head[from],w));
    50     tmp_head[from]=++cnt;
    51 }
    52 
    53 void dfs_tree(int x,int fa){
    54     for(int ee=head[x];ee;ee=edges[ee].next)
    55         if(edges[ee].to!=fa){
    56             add_tree(x,edges[ee].to,edges[ee].w);
    57             dfs_tree(edges[ee].to,x);
    58         }
    59 }
    60 
    61 void dfs(int ss,lb ww){
    62     data[ss]+=ww;
    63     for(int ee=head[ss];ee;ee=tree[ee].next)
    64         if(oo(tree[ee].w))
    65             dfs(tree[ee].to,ww*tree[ee].w);
    66 }
    67 
    68 int main(){
    69     //freopen("temp.in","r",stdin);
    70     cnt=0;
    71     n=read();
    72     edges.push_back(edge(0,0,0));
    73     for(int i=1;i<n;i++){
    74         int from=read(),to=read();
    75         lb w;scanf("%Lf",&w);
    76         add_edge(from,to,w);
    77     }
    78     cnt=0;
    79     tree.push_back(edge(0,0,0));
    80     dfs_tree(root,0);
    81     swap(head,tmp_head);
    82     m=read();
    83     for(int i=0;i<m;i++){
    84         int op=read(),ss=read();
    85         if(op&8){
    86             printf("%.8Lf
    ",data[ss]);
    87         }
    88         else{
    89             lb ww;scanf("%Lf",&ww);
    90             dfs(ss,ww);
    91         }
    92     }
    93     return 0;
    94 }

    100分代码

    ...

    2333333

    我怎么这么傻。我的图论怎么这么弱呢

    正解提供的思路是这样的

    既然题目中有类似“从根到子树”的操作,可以很快地想到利用dfs序解决。好了就dfs序吧!然后把wi=0的边全部砍断,就得到了几棵互不关联的树。那么怎么砍断呢?只要将wi=0的边连向的点记录起来,分别从根(root=1)和这些被记录的节点做dfs序(走完一棵子树后序号不清零,要继续累加),就得到了我们需要的dfs序列。

    维护dfs序的同时,维护一个k[]数组,ki表示从i节点所在子树的根到i节点一路上wi的累乘结果。

    然后用树状数组(解决区间加和点询问问题的树状数组)维护所有节点。

    对于操作①

      假如从一棵子树的根释放了冷气x,那么冷气一定到达这个子树的每一个节点,每一个节点i得到的冷气值就是ki*x,结合dfs序对表示这棵子树的区间加x就好了。那么对于一个普通节点i(非子树的根节点)释放的冷气x,就可以等同于从这棵子树根释放的冷气x/ki,对表示节点i的子树的dfs序中的一段加x/ki就好了。

      根据树状数组的性质时间复杂度为O(logn)

    对于操作⑨

      操作①把重要的事都解决了,操作⑨只需要在树状数组上求出节点i得到的值x,输出x*ki即可。

      根据树状数组的性质时间复杂度为O(logn)

    所以总的时间复杂度为O(mlogn),是可以解决问题的。

    附上std的代码(5309ms)和我的代码(3954ms)。。良好的代码习惯可以压到2500-ms

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<queue>
     6 #include<cmath>
     7 #define ld long double
     8 using namespace std;
     9 int n,m;
    10 const ld eps=1e-8;
    11 vector<int>tab[100011];
    12 vector<ld>val[100011];
    13 ld k[100011];
    14 ld tree[100011];
    15 int ino[100011];
    16 int outo[100011];
    17 int fa[100011];
    18 int tot=0;
    19 queue<int>q;
    20 void add(int x,ld t)
    21 {
    22     for(x;x<=n;x+=x&-x)
    23         tree[x]+=t;
    24 }
    25 ld query(int x)
    26 {
    27     ld res=0;
    28     for(x;x>0;x-=x&-x)
    29         res+=tree[x];
    30     return res;
    31 }
    32 void dfs(int now,int father,long double ki)
    33 {
    34     ino[now]=++tot;
    35     fa[now]=father;
    36     k[now]=ki;
    37     int sz=tab[now].size();
    38     for(int i=0;i<sz;++i)
    39     {
    40         int nex=tab[now][i];
    41         if(nex==fa[now])continue;
    42         if(fabs(val[now][i])<eps)
    43         {
    44             fa[nex]=now;
    45             q.push(nex);
    46             continue;
    47         }
    48         dfs(nex,now,ki*val[now][i]);
    49     }
    50     outo[now]=tot;
    51 }
    52 int main()
    53 {
    54     scanf("%d",&n);
    55     for(int i=1;i<n;++i)
    56     {
    57         int u,v;ld w;
    58         scanf("%d%d%Lf",&u,&v,&w);
    59         tab[u].push_back(v);
    60         tab[v].push_back(u);
    61         val[u].push_back(w);
    62         val[v].push_back(w); 
    63     }
    64     q.push(1);
    65     while(!q.empty())
    66     {
    67         dfs(q.front(),fa[q.front()],1.0);
    68         q.pop();
    69     }
    70     scanf("%d",&m);
    71     while(m--)
    72     {
    73         int typ,i;
    74         ld x;
    75         scanf("%d",&typ);
    76         if(typ==1)
    77         {
    78             scanf("%d%Lf",&i,&x);
    79             ld ins=x/k[i];
    80             add(ino[i],ins);
    81             add(outo[i]+1,-ins);
    82         }else{
    83             scanf("%d",&i);
    84             printf("%.8Lf
    ",query(ino[i])*k[i]);
    85         }
    86     }
    87     return 0;
    88 } 

     我的辣2333

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<iostream>
      4 #include<queue>
      5 #include<vector>
      6 #include<cmath>
      7 using namespace std;
      8 
      9 inline int read(){
     10     char ch;
     11     int re=0;
     12     bool flag=0;
     13     while((ch=getchar())!='-'&&(ch<'0'||ch>'9'));
     14     ch=='-'?flag=1:re=ch-'0';
     15     while((ch=getchar())>='0'&&ch<='9')  re=(re<<1)+(re<<3)+ch-'0';
     16     return flag?-re:re;
     17 }
     18 
     19 typedef long double ld;
     20 typedef pair<int,int> PII;
     21 
     22 struct edge{
     23     int to,next;
     24     ld w;
     25     edge(int to=0,int next=0,ld w=0):
     26         to(to),next(next),w(w){}
     27 };
     28 
     29 const int maxn=100001;
     30 
     31 vector<edge> edges;
     32 int n,m,cnt=0,root=1,head[maxn],id[maxn],son[maxn];
     33 ld kk[maxn],bit[maxn];
     34 queue<PII> que;
     35 
     36 inline void add_edge(int from,int to,ld w){
     37     edges.push_back(edge(to,head[from],w));
     38     head[from]=++cnt;
     39     edges.push_back(edge(from,head[to],w));
     40     head[to]=++cnt;
     41 }
     42 
     43 ld eps=1e-8;
     44 bool oo(ld w){
     45     if(fabs(w)<eps)  return 0;
     46     return 1;
     47 }
     48 
     49 void init(){
     50     n=read();
     51     edges.push_back(edge(0,0,0));
     52     for(int i=1;i<n;i++){
     53         int from=read(),to=read();
     54         ld w;scanf("%Lf",&w);
     55         add_edge(from,to,w);
     56     }
     57 }
     58 
     59 void dfs(int x,int fa,ld kkk){
     60     id[x]=++cnt;
     61     son[x]=1;
     62     kk[x]=kkk;
     63     for(int ee=head[x];ee;ee=edges[ee].next)
     64         if(edges[ee].to!=fa)
     65             if(!oo(edges[ee].w))
     66                 que.push(make_pair(edges[ee].to,x));
     67             else{
     68                 dfs(edges[ee].to,x,kkk*edges[ee].w);
     69                 son[x]+=son[edges[ee].to];
     70             }
     71 }
     72 
     73 ld getsum(int ss){
     74     int sit=id[ss];
     75     ld sum=0;
     76     while(sit<=n){
     77         sum+=bit[sit];
     78         sit+=sit&-sit;
     79     }
     80     return sum;
     81 }
     82 
     83 void add_it(int sit,ld xx){
     84     while(sit){
     85         bit[sit]+=xx;
     86         sit-=sit&-sit;
     87     }
     88 }
     89 
     90 void add(int left,int right,ld xx){
     91     if(left-1)
     92         add_it(left-1,-xx);
     93     add_it(right,xx);
     94 }
     95 
     96 void solve(){
     97     m=read();
     98     for(int i=0;i<m;i++){
     99         int op=read(),ss=read();
    100         if(op&8)
    101             printf("%.8Lf
    ",getsum(ss)*kk[ss]);
    102         else{
    103             ld xx;scanf("%Lf",&xx);
    104             add(id[ss],id[ss]+son[ss]-1,xx/kk[ss]);
    105         }
    106     }
    107 }
    108 
    109 int main(){
    110     //freopen("temp.in","r",stdin);
    111     init();
    112     que.push(make_pair(root,0));
    113     cnt=0;
    114     while(!que.empty()){
    115         PII x=que.front();  que.pop();
    116         dfs(x.first,x.second,1.0);
    117     }
    118     solve();
    119     return 0;
    120 }
  • 相关阅读:
    集训总结
    目前世界四大杀毒软件各自的特点
    俺们家老祖宗是搞音乐的
    灰姑娘
    自欺欺人的ie7pro去掉msnlivespaces的顶部广告图片
    利用工具及api接口写博文
    在C#中,异步调用的WebService、Socket及新建线程中操作主线程中UI的方法
    Ubuntu 12.04下载址
    js与php互传参数
    php使用strstr函数 ,判断字符串A中是否含有字符串B
  • 原文地址:https://www.cnblogs.com/ZYBGMZL/p/6883638.html
Copyright © 2020-2023  润新知