[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\n",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\n",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\n",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 }

 

posted @ 2017-05-20 23:52  ZYBGMZL  阅读(400)  评论(0编辑  收藏  举报