[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的西瓜的寒冷程度值。
输出格式:
对于每个操作⑨,输出一行一个实数,表示对应西瓜的寒冷程度值。
输入输出样例
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
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 }