[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
好小的树
--->朴素的、完全依照题意的遍历树算法
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<cmath>
using namespace std; inline int read(){
int re=;
char ch;
bool flag=;
while((ch=getchar())!='-'&&(ch<''||ch>''));
ch=='-'?flag=:re=ch-'';
while((ch=getchar())>=''&&ch<='') re=(re<<)+(re<<)+ch-'';
return flag?-re:re;
} typedef long double lb; struct edge{
int to,next;
lb w;
edge(int to=,int next=,lb w=):
to(to),next(next),w(w){}
}; const int maxn=; const lb eps=1e-;
vector<edge> edges;
vector<edge> tree;
int n,m,cnt,root=;
int head[maxn],tmp_head[maxn];
lb data[maxn]; bool oo(lb ww){
if(fabs(ww)<eps) return ;
return ;
} inline void add_edge(int from,int to,lb w){
edges.push_back(edge(to,head[from],w));
head[from]=++cnt;
edges.push_back(edge(from,head[to],w));
head[to]=++cnt;
} inline void add_tree(int from,int to,lb w){
tree.push_back(edge(to,tmp_head[from],w));
tmp_head[from]=++cnt;
} void dfs_tree(int x,int fa){
for(int ee=head[x];ee;ee=edges[ee].next)
if(edges[ee].to!=fa){
add_tree(x,edges[ee].to,edges[ee].w);
dfs_tree(edges[ee].to,x);
}
} void dfs(int ss,lb ww){
data[ss]+=ww;
for(int ee=head[ss];ee;ee=tree[ee].next)
if(oo(tree[ee].w))
dfs(tree[ee].to,ww*tree[ee].w);
} int main(){
//freopen("temp.in","r",stdin);
cnt=;
n=read();
edges.push_back(edge(,,));
for(int i=;i<n;i++){
int from=read(),to=read();
lb w;scanf("%Lf",&w);
add_edge(from,to,w);
}
cnt=;
tree.push_back(edge(,,));
dfs_tree(root,);
swap(head,tmp_head);
m=read();
for(int i=;i<m;i++){
int op=read(),ss=read();
if(op&){
printf("%.8Lf\n",data[ss]);
}
else{
lb ww;scanf("%Lf",&ww);
dfs(ss,ww);
}
}
return ;
}
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
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#define ld long double
using namespace std;
int n,m;
const ld eps=1e-;
vector<int>tab[];
vector<ld>val[];
ld k[];
ld tree[];
int ino[];
int outo[];
int fa[];
int tot=;
queue<int>q;
void add(int x,ld t)
{
for(x;x<=n;x+=x&-x)
tree[x]+=t;
}
ld query(int x)
{
ld res=;
for(x;x>;x-=x&-x)
res+=tree[x];
return res;
}
void dfs(int now,int father,long double ki)
{
ino[now]=++tot;
fa[now]=father;
k[now]=ki;
int sz=tab[now].size();
for(int i=;i<sz;++i)
{
int nex=tab[now][i];
if(nex==fa[now])continue;
if(fabs(val[now][i])<eps)
{
fa[nex]=now;
q.push(nex);
continue;
}
dfs(nex,now,ki*val[now][i]);
}
outo[now]=tot;
}
int main()
{
scanf("%d",&n);
for(int i=;i<n;++i)
{
int u,v;ld w;
scanf("%d%d%Lf",&u,&v,&w);
tab[u].push_back(v);
tab[v].push_back(u);
val[u].push_back(w);
val[v].push_back(w);
}
q.push();
while(!q.empty())
{
dfs(q.front(),fa[q.front()],1.0);
q.pop();
}
scanf("%d",&m);
while(m--)
{
int typ,i;
ld x;
scanf("%d",&typ);
if(typ==)
{
scanf("%d%Lf",&i,&x);
ld ins=x/k[i];
add(ino[i],ins);
add(outo[i]+,-ins);
}else{
scanf("%d",&i);
printf("%.8Lf\n",query(ino[i])*k[i]);
}
}
return ;
}
我的辣2333
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<vector>
#include<cmath>
using namespace std; inline int read(){
char ch;
int re=;
bool flag=;
while((ch=getchar())!='-'&&(ch<''||ch>''));
ch=='-'?flag=:re=ch-'';
while((ch=getchar())>=''&&ch<='') re=(re<<)+(re<<)+ch-'';
return flag?-re:re;
} typedef long double ld;
typedef pair<int,int> PII; struct edge{
int to,next;
ld w;
edge(int to=,int next=,ld w=):
to(to),next(next),w(w){}
}; const int maxn=; vector<edge> edges;
int n,m,cnt=,root=,head[maxn],id[maxn],son[maxn];
ld kk[maxn],bit[maxn];
queue<PII> que; inline void add_edge(int from,int to,ld w){
edges.push_back(edge(to,head[from],w));
head[from]=++cnt;
edges.push_back(edge(from,head[to],w));
head[to]=++cnt;
} ld eps=1e-;
bool oo(ld w){
if(fabs(w)<eps) return ;
return ;
} void init(){
n=read();
edges.push_back(edge(,,));
for(int i=;i<n;i++){
int from=read(),to=read();
ld w;scanf("%Lf",&w);
add_edge(from,to,w);
}
} void dfs(int x,int fa,ld kkk){
id[x]=++cnt;
son[x]=;
kk[x]=kkk;
for(int ee=head[x];ee;ee=edges[ee].next)
if(edges[ee].to!=fa)
if(!oo(edges[ee].w))
que.push(make_pair(edges[ee].to,x));
else{
dfs(edges[ee].to,x,kkk*edges[ee].w);
son[x]+=son[edges[ee].to];
}
} ld getsum(int ss){
int sit=id[ss];
ld sum=;
while(sit<=n){
sum+=bit[sit];
sit+=sit&-sit;
}
return sum;
} void add_it(int sit,ld xx){
while(sit){
bit[sit]+=xx;
sit-=sit&-sit;
}
} void add(int left,int right,ld xx){
if(left-)
add_it(left-,-xx);
add_it(right,xx);
} void solve(){
m=read();
for(int i=;i<m;i++){
int op=read(),ss=read();
if(op&)
printf("%.8Lf\n",getsum(ss)*kk[ss]);
else{
ld xx;scanf("%Lf",&xx);
add(id[ss],id[ss]+son[ss]-,xx/kk[ss]);
}
}
} int main(){
//freopen("temp.in","r",stdin);
init();
que.push(make_pair(root,));
cnt=;
while(!que.empty()){
PII x=que.front(); que.pop();
dfs(x.first,x.second,1.0);
}
solve();
return ;
}
[luogu P3787][新创无际夏日公开赛] 冰精冻西瓜 [树状数组][dfs序]的更多相关文章
- luogu SP8093 后缀自动机+树状数组+dfs序
这题解法很多,简单说几个: 1. 线段树合并,时间复杂度是 $O(nlog^2n)$ 的. 2. 暴力跳 $fail,$ 时间复杂度 $O(n\sqrt n),$ 比较暴力. 3. 建立后缀树后在 $ ...
- 【Luogu P5168】xtq玩魔塔(Kruskal 重构树 & 树状数组 & set)
Description 给定一个 \(n\) 个顶点,\(m\) 条边的无向联通图,点.边带权. 先有 \(q\) 次修改或询问,每个指令形如 \(\text{opt}\ x\ y\): \(\tex ...
- BZOJ 2434 Luogu P2414 [NOI2011]阿狸的打字机 (AC自动机、树状数组)
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=2434 题解: 我写的是离线做法,不知道有没有在线做法. 转化一波题意,\(x\)在AC ...
- [洛谷 P3787] 冰精冻西瓜
题目描述 琪露诺是拥有操纵冷气程度的能力的妖精,一天她发现了一片西瓜地.这里有n个西瓜,由n-1条西瓜蔓连接,形成一个有根树,琪露诺想要把它们冷冻起来慢慢吃. 这些西瓜蔓具有神奇的性质,可以将经过它的 ...
- 冰精冻西瓜[P3787洛谷]
题目描述 琪露诺是拥有操纵冷气程度的能力的妖精,一天她发现了一片西瓜地.这里有n个西瓜,由n-1条西瓜蔓连接,形成一个有根树,琪露诺想要把它们冷冻起来慢慢吃. 这些西瓜蔓具有神奇的性质,可以将经过它的 ...
- Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分)
Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分) Description L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之 ...
- Luogu 45887 全村最好的嘤嘤刀(线段树 树状数组)
https://www.luogu.org/problemnew/show/T45887 题目背景 重阳节到了,我们最好的八重樱拥有全村最好的嘤嘤刀…… 题目描述 在绯玉丸力量的影响下,八重村成了一条 ...
- luogu P1972 [SDOI2009]HH的项链 |树状数组 或 莫队
题目描述 HH 有一串由各种漂亮的贝壳组成的项链.HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义.HH 不断地收集新的贝壳,因此,他的项链变得越来越长. ...
- luogu P3031 [USACO11NOV]高于中位数Above the Median (树状数组优化dp)
链接:https://www.luogu.org/problemnew/show/P3031 题面: 题目描述 Farmer John has lined up his N (1 <= N &l ...
随机推荐
- Java Web入门学习(四)Eclipse与Maven、Tomcat整合配置
Java Web学习(四)Eclipse与Maven整合配置 一.准备工作 1.Tomcat 8.5.15 2.Maven3.5 3.Eclipse Neon.3 Release (4.6.3) 二. ...
- pod trunk push --verbose 失败的原因总结
用 pod trunk push --verbose 添加一个 pod 的时候,经常出现如下的错误 [!] The podspec does not validate. /Library/Ruby/ ...
- B树和B+树的总结
B树 为什么要B树 磁盘中有两个机械运动的部分,分别是盘片旋转和磁臂移动.盘片旋转就是我们市面上所提到的多少转每分钟,而磁盘移动则是在盘片旋转到指定位置以后,移动磁臂后开始进行数据的读写.那么这就存在 ...
- Dom 简介
HTML DOM 简介 DOM 教程 DOM 节点 HTML DOM 定义了访问和操作 HTML 文档的标准. 您应该具备的基础知识 在您继续学习之前,您需要对以下内容拥有基本的了解: HTML CS ...
- Bash的命令替换
命令替换:将命令替换为命令的输出,所有的shell支持使用反引号的方法进行命令替换.Bash支持两种形式:$(command) 和`command`命令替换是可以嵌套的,如果使用反引号的形式,在内部反 ...
- Jenkin-持续集成
1.Jenkins安装 本文将会介绍如何在windows 中安装Jenkins,并且使用Jenkins进行项目的构建. 首先我们进入到Jenkins 的官网下载地址:https://jenkins.i ...
- 腾讯AlloyTeam正式发布pasition - 制作酷炫Path过渡动画
pasition Pasition - Path Transition with little JS code, render to anywhere - 超小尺寸的Path过渡动画类库 Github ...
- C#中==运算符
在这篇博客中,我们将介绍如下内容: ==运算符与基元类型 ==运算符与引用类型 ==运算符与String类型 ==运算符与值类型 ==运算符与泛型 ==运算符与基元类型 我们分别用两种方式比较两个整数 ...
- Testlink研究小结
1.Redmine与Testlink的关联 (1)redmine中的项目对应testlink的测试项目 (2)testllink执行用例时发现的问题可以记录到redmine中 2.Testlink优点 ...
- [USACO15JAN]电影移动Moovie Mooving
[USACO15JAN]电影移动Moovie Mooving 时间限制: 2 Sec 内存限制: 128 MB 题目描述 Bessie is out at the movies. Being mis ...