染色
来源:互联网 发布:java socket 心跳实例 编辑:程序博客网 时间:2024/04/26 05:34
题目大意
给定一颗树,每个点默认白色,有两种操作。
把一个点染黑(不保证此时该点为白色)
询问一个点与所有黑点的距离和
树剖
先把原树转化为有根树。
然后询问一个点u,就是枚举每一个黑点v
然后贡献为d[u]+d[v]-2*d[lca(u,v)]
记录黑点的总个数和深度和,前两项很容易求和,最后一项呢?
我们尝试枚举u到root路径上的每一点w,设size[w]表示w是多少个黑点的祖先。
那么贡献为f[w]*size[w],其中f[i]表示i到父亲的距离。
然后明显可以用树链剖分搞了。
两个log不优美
其实这道题我们可以做到一个log。
考虑点剖:
belong[i,j]表示分治到第j层i属于分治中心的哪个子树内。
d[i,j]表示分治到第j层i到分治中心的距离。
cen[i,j]表示分治到第j层i所属的分治中心。
ans[j]表示j作为分治中心时以其为根的子树内所有黑点到其的距离和。
num[i,j]表示分治到第j层以i为根的子树内所有黑点到分治中心的距离和(i与分治中心有一条边直接相连)
cnt[j]表示j作为分治中心时以其为根的子树内黑点的个数(不考虑分治中心是否为黑点)
sum[i,j]表示分治到第j层以i为根的子树内黑点的个数(i与分治中心有一条边直接相连)
询问操作(询问x到所有黑点的距离和):
从小到大枚举层数j。
如果在第j层x是分治中心则答案加上ans[x]并退出。
否则,答案加上ans[cen[x,j]]-num[belong[x,j],j]+d[x,j]*(cnt[cen[x,j]]-sum[belong[x,j],j])。
然后,若分治中心(即cen[x,j])为黑点,再给答案加上d[x,j]。
感觉这题解就是代码即代码注释(囧
修改操作(把x染黑):
x是黑点那我们啥也不干。
从小到大枚举层数j。
如果第j层x是分治中心就退出。
否则更新一下ans、num、cnt、sum。
详见代码QAQ。
#include<cstdio>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;typedef long long ll;const int maxn=100000+10;int belong[maxn][20],cen[maxn][20],size[maxn],a[maxn],dep[maxn],cnt[maxn];int h[maxn],go[maxn*2],next[maxn*2],sum[maxn][20];ll d[maxn][20],num[maxn][20],dis[maxn*2],ans[maxn];bool bz[maxn];int i,j,k,l,t,n,m,x,tot,top;ll now;void add(int x,int y,int z){ go[++tot]=y; dis[tot]=z; next[tot]=h[x]; h[x]=tot;}void dfs(int x,int y){ a[++top]=x; int t=h[x]; size[x]=1; while (t){ if (go[t]!=y&&!bz[go[t]]){ dfs(go[t],x); size[x]+=size[go[t]]; } t=next[t]; }}void dg(int x,int y,int z,int d){ belong[x][d]=z; int t=h[x]; while (t){ if (go[t]!=y&&!bz[go[t]]){ dep[go[t]]=dep[x]+dis[t]; dg(go[t],x,z,d); } t=next[t]; }}void solve(int x,int y){ top=0; dfs(x,0); int i,j=x,k=0,t; while (1){ t=h[j]; while (t){ if (go[t]!=k&&!bz[go[t]]&&size[go[t]]>top/2){ k=j; j=go[t]; break; } t=next[t]; } if (!t) break; } dep[j]=0; t=h[j]; while (t){ if (!bz[go[t]]){ dep[go[t]]=dep[j]+dis[t]; dg(go[t],j,go[t],y); } t=next[t]; } fo(i,1,top) d[a[i]][y]=dep[a[i]],cen[a[i]][y]=j; bz[j]=1; t=h[j]; while (t){ if (!bz[go[t]]) solve(go[t],y+1); t=next[t]; }}int main(){ freopen("color1.in","r",stdin); scanf("%d%d",&n,&m); fo(i,2,n) scanf("%d",&a[i]),a[i]++; fo(i,2,n) scanf("%d",&dep[i]); fo(i,2,n) add(i,a[i],dep[i]),add(a[i],i,dep[i]); solve(1,0); fo(i,1,n) bz[i]=0; while (m--){ scanf("%d%d",&t,&x); x++; if (t==1){ if (bz[x]) continue; j=0; bz[x]=1; while (cen[x][j]!=x){ ans[cen[x][j]]+=d[x][j]; num[belong[x][j]][j]+=d[x][j]; cnt[cen[x][j]]++; sum[belong[x][j]][j]++; j++; } } else{ now=j=0; while (1){ if (cen[x][j]==x){ now+=ans[x]; break; } else{ now+=ans[cen[x][j]]-num[belong[x][j]][j]; now+=(ll)d[x][j]*(cnt[cen[x][j]]-sum[belong[x][j]][j]); if (bz[cen[x][j]]) now+=d[x][j]; } j++; } printf("%lld\n",now); } }}
后续
我发现wyx的代码又短又快!
点进去是个大暴力!
我仔细思考这个问题并得到了结论——
i的父亲的编号小于i,这样随机出来的树期望高度为log n!
而这题就是采取了这种随机方法!
(出题人居然全随机数据……
- 染色
- 染色
- 染色
- [SDOI2011]染色
- 染色(paint)
- 选拔赛-染色
- 【SDOI2011】染色
- hybz2243 染色
- 数轴染色
- 染色配对
- 染色日志
- 栅栏染色
- 房屋染色
- bzoj2698: 染色
- 【SDOI2011】染色
- 染色法
- 【SDOI2011】染色
- 染色问题
- 5.1 抽象化
- markdown(二)语法
- ubuntu14.04 安装 搜狗输入法
- 老戚的黑科技之SSH隧道技术
- 管理软件的欺骗式营销
- 染色
- Android编程之内存溢出解决方案(OOM)实例总结
- asp.net 统计登录的用户数
- shell--shift左移参数、函数
- 【Qt】QComboBox
- 最大的算式
- 在 VS 中编译项目时出现 error LNK2005 链接错误的解决办法
- dreamwear中创建li:hover
- 程序员小笑话