[BZOJ3683]Falsita

题目大意:

一个\(n(n\le3\times10^5)\)个结点的树,每个结点有一个权值\(w_i\),\(m(m\le3\times10^5)\)次操作,操作包含以下\(3\)种:

  1. 将结点\(u\)的权值加上\(d\);
  2. 将以\(u\)为根的子树中的每一个结点加上\(d\);
  3. 询问任取一个以\(u\)为LCA的点对\((x,y)\),\(w_x+w_y\)的期望值。

思路:

首先可以用一遍树形DP求出不考虑修改的答案\(ans_i\)。

对于操作\(1\),会对\(u\)本身的答案产生\(d\times(size[u]-1)\)的贡献。对\(par[u]\)到根上每个结点\(x\)的贡献为\(d\times(size[x]-size[y])\),其中\(y\)为\(x\)在链上的子结点。

对于操作\(2\),会对\(u\)子树内每个结点产生\(2d\times pair[x]\)的贡献,其中\(pair[u]\)为以\(u\)为LCA的点对数。对到根的链上结点贡献为\(d\times(size[x]-size[y])\times size[u]\)。

由于对于每个结点,\((size[x]-size[y])\)和\(pair[x]\)都是固定的,因此对于链上修改和子树修改,我们只需要维护\(d\)即可。这可以用树链剖分+线段树实现。

时间复杂度\(\mathcal O(n\log^2n)\)。

源代码:

#include<cstdio>
#include<cctype>
#include<vector>
inline int getint() {
register char ch;
register bool neg=false;
while(!isdigit(ch=getchar())) neg|=ch=='-';
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return neg?-x:x;
}
inline char getalpha() {
register char ch;
while(!isalpha(ch=getchar()));
return ch;
}
typedef long long int64;
const int N=3e5+1;
int par[N],w[N],size[N],dep[N],son[N],dfn[N],top[N];
int64 pair[N],ans[N],sum[N];
std::vector<int> e[N];
inline void add_edge(const int &u,const int &v) {
e[u].push_back(v);
}
void dfs(const int &x,const int &par) {
size[x]=1;
sum[x]=w[x];
dep[x]=dep[par]+1;
for(unsigned i=0;i<e[x].size();i++) {
const int &y=e[x][i];
dfs(y,x);
sum[x]+=sum[y];
pair[x]+=1ll*size[x]*size[y];
size[x]+=size[y];
if(size[y]>size[son[x]]) {
son[x]=y;
}
}
ans[x]=1ll*w[x]*(size[x]-1);
for(register unsigned i=0;i<e[x].size();i++) {
const int &y=e[x][i];
ans[x]+=1ll*sum[y]*(size[x]-size[y]);
}
}
void dfs(const int &x) {
dfn[x]=++dfn[0];
top[x]=x==son[par[x]]?top[par[x]]:x;
if(son[x]) dfs(son[x]);
for(unsigned i=0;i<e[x].size();i++) {
const int &y=e[x][i];
if(y==son[x]) continue;
dfs(y);
}
}
class SegmentTree {
#define _left <<1
#define _right <<1|1
#define mid ((b+e)>>1)
private:
int64 val[N<<2];
public:
void add(const int &p,const int &b,const int &e,const int &l,const int &r,const int64 &d) {
if(b==l&&e==r) {
val[p]+=d;
return;
}
if(l<=mid) add(p _left,b,mid,l,std::min(mid,r),d);
if(r>mid) add(p _right,mid+1,e,std::max(mid+1,l),r,d);
}
int64 query(const int &p,const int &b,const int &e,const int &x) const {
int64 ret=val[p];
if(b==e) return ret;
if(x<=mid) ret+=query(p _left,b,mid,x);
if(x>mid) ret+=query(p _right,mid+1,e,x);
return ret;
}
#undef _left
#undef _right
#undef mid
};
SegmentTree t1,t2;
inline void modify0(int x,const int64 &d) {
//链上修改
while(x) {
if(x!=top[x]) t1.add(1,1,dfn[0],dfn[top[x]],dfn[par[x]],d);
x=top[x];
if(par[x]!=0) ans[par[x]]+=d*(size[par[x]]-size[x]);
x=par[x];
}
}
inline void modify1(int x,const int64 &d) {
//单点修改
ans[x]+=d*(size[x]-1);
modify0(x,d);
}
inline void modify2(const int &x,const int64 &d) {
//子树修改
if(size[x]>1) t2.add(1,1,dfn[0],dfn[x],dfn[x]+size[x]-1,d);
modify0(x,d*size[x]);
}
inline double query(const int &x) {
return 1.*(ans[x]+1ll*t1.query(1,1,dfn[0],dfn[x])*(size[x]-size[son[x]])+2ll*t2.query(1,1,dfn[0],dfn[x])*pair[x])/pair[x];
}
int main() {
const int n=getint(),m=getint();
for(register int i=2;i<=n;i++) {
add_edge(par[i]=getint(),i);
}
for(register int i=1;i<=n;i++) {
w[i]=getint();
}
dfs(1,0);
dfs(1);
for(register int i=0;i<m;i++) {
const char opt=getalpha();
const int u=getint();
if(opt=='S') modify1(u,getint());
if(opt=='M') modify2(u,getint());
if(opt=='Q') printf("%.6f\n",query(u));
}
return 0;
}

[BZOJ3683]Falsita的更多相关文章

  1. NKOJ-4573 Falsita

    问题描述: 到海边了呢...... 如果没有那次选择,现在是不是会好些呢...... 都过去了. 仰望着星空,迎面吹过一阵阵海风,倚靠着护栏,Fine 在海边静静地伫立着,在一个个无际的长夜后,Fin ...

  2. POJ3683 Falsita

    http://poj.org/problem?id=3683 思路:2-SAT,输出任意一组方案,O(m+n) #include<cstdio> #include<iostream& ...

  3. 【BZOJ】3683: Falsita

    题解 这道题维护方法比较简单,也有点奇妙 我们可以很容易求出经过所有点的路径条数,和初始时分子的大小 然后单点修改的时候,相当于给当前点\(v\)加上\(delta * (siz[v] - 1)\) ...

  4. CH Round #51 - Shinrein祭 #1

    A.Phorni 题目:http://www.contesthunter.org/contest/CH%20Round%20%2351%20-%20Shinrein祭%20%231/Phorni 没做 ...

  5. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

随机推荐

  1. appium+python测试app使用相对坐标定位元素

    我们获取到的是绝对坐标,如果换一个屏幕分辨率不同的手机那这个坐标自然会发生变化,要实现不同手机均能实现点击同一控件自然要用到相对坐标了,具体方法如下: 1.获取当前空间的绝对坐标(x1,y1),开启指 ...

  2. spring cloud 路由网关zuul的高可用

    Zuul的高可用非常关键,因为外部请求到后端微服务的流量都会经过Zuul.故而在生产环境中,我们一般都需要部署高可用的Zuul以避免单点故障. 笔者分两种场景讨论Zuul的高可用. Zuul客户端也注 ...

  3. lsattr chattr 文件安全设置

    [root@test_android_client_download xianlaigames]# lsattr -------------e- ./jilinmj.apk[root@test_and ...

  4. Web.Config引入配置ConfigSource

    1.配置文件要和Config文件通一个项目 2.注意路径的写法 3.appSettings和connectionStrings等都可以设置configSource 4.这样发布到不同的环境的时候,改动 ...

  5. nginx 正则及rewrite常用规则实例

    一.正则表达式匹配,其中:* ~ 为区分大小写匹配* ~* 为不区分大小写匹配* !~和!~*分别为区分大小写不匹配及不区分大小写不匹配二.文件及目录匹配,其中:* -f和!-f用来判断是否存在文件* ...

  6. Windows下80端口被进程System占用的解决方法

    最近电脑时不时就发生了80端口被占用的情况,简单百度解决后,当重启电脑的时候又发生被占用的情况.今天非常幸运的是,发生了80端口和8080端口都被占用了情况,忍无可忍决定下定决心解决这个坑爹的问题,经 ...

  7. parted 分区命令

    fdisk  是针对 MBR的分区 ,因为MBR分区空间最大不能超过2T  最多分4个主分区 , 所以parted可以修改磁盘为GPT  可以支持更大的分区,更多的分区 1  查看分区 : #part ...

  8. Python学习(十八)—— 数据库(三)

    转载自http://www.cnblogs.com/linhaifeng/articles/7356064.html 一.数据操作 1.插入数据INSERT 1. 插入完整数据(顺序插入) 语法一: ...

  9. nacos-server集群 安装、运行(ubuntu)

    下载.解压 wget -P /opt/downloads https://github.com/alibaba/nacos/releases/download/1.0.0/nacos-server-1 ...

  10. Substrings kmp

    Problem Description You are given a number of case-sensitive strings of alphabetic characters, find ...