bzoj3307 雨天的尾巴题解及改题过程(线段树合并+lca+树上差分)

题目描述

N个点,形成一个树状结构。有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品。完成所有发放后,每个点存放最多的是哪种物品。

输入格式

第一行数字N,M
接下来N-1行,每行两个数字a,b,表示a与b间有一条边
再接下来M行,每行三个数字x,y,z.如题

输出格式

输出有N行
每i行的数字表示第i个点存放最多的物品是哪一种,如果有
多种物品的数量一样,输出编号最小的。如果某个点没有物品则输出0

 ----------------------------------van美分界线----------------------------------

先%一发pa大佬考试A掉这题

%%%pa

考试时刚看到这题时觉得和之前的考试题松鼠的新家(此坑未填)很像,因为都是对树上的一条链进行修改操作

所以很容易想到树上差分(其实树剖也可以但蒟蒻博主并不会),具体讲就是将链的两端加一,将lca和lca父亲节点减一。

然后我们可以看到他是询问数量所以可以想到在每一个节点建一棵权值线段树来维护信息。

又看到1e9的范围瞬间吓尿,跑去码T1,其实只要离散化一下就可以,因此我们不仅需要维护每一个节点的最大值,还要维护最大值出现的位置,这样比较方便输出答案,建立对应关系即可。

最后dfs统计答案即可,就是从叶节点往上不断merge。

最后要注意的一点就是和线段树有关的数组一定要开大一些,本人亲测要1e5×60,临接表数组开二倍(都这时候了我还犯这么低级错误,真沙雕)。

回想一下这题也没那么难,但我还是断断续续调了得有5.6节课,沙雕错误百出。具体沙雕错误代码里都有注释(大佬自动忽略即可,勿喷)。

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 #include<cmath>
  5 #include<algorithm>
  6 #include<queue>
  7 using namespace std;
  8 const int N=1e5+10;
  9 int n,m;int tot;int t;
 10 int first[N],nex[N*2],to[N*2],cnt,d[N],root[N*20*3],v[N],f[N][22],sum[N*20*3],posm[N*20*3],ls[N*20*3],rs[N*20*3],ans[N],x[N],yy[N],zz[N],num[N];
 11 void add(int a,int b){
 12     to[++tot]=b;nex[tot]=first[a];first[a]=tot;
 13 }
 14 void bfs(int x){
 15     queue<int> q;
 16     q.push(x);d[x]=1;
 17     while(!q.empty()){
 18         int x=q.front();q.pop();
 19         for(int i=first[x];i;i=nex[i]){
 20             int y=to[i];
 21             if(d[y]) continue;
 22             d[y]=d[x]+1;
 23             f[y][0]=x;
 24             for(int j=1;j<=t;j++)
 25                 f[y][j]=f[f[y][j-1]][j-1];
 26             q.push(y);
 27         } 
 28     }
 29 }
 30 int Lca(int x,int y){
 31     if(d[y]<d[x]) swap(x,y);
 32     for(int i=t;i>=0;i--){
 33         if(d[f[y][i]]>=d[x]) y=f[y][i];
 34     }
 35     if(x==y) return x;
 36     for(int i=t;i>=0;i--){
 37         if(f[y][i]!=f[x][i]) x=f[x][i],y=f[y][i];
 38     }
 39     return f[x][0];
 40 }
 41 void pushup(int x){
 42     if(sum[ls[x]]>=sum[rs[x]]) sum[x]=sum[ls[x]],posm[x]=posm[ls[x]];
 43     else sum[x]=sum[rs[x]],posm[x]=posm[rs[x]];
 44 }
 45 void update(int &x,int z,int add,int l,int r){
 46     if(!x){
 47         x=++cnt;
 48     }
 49     if(l==r){
 50         sum[x]+=add;posm[x]=z/*z !l*/;
 51         return;//void  return sbsbsbsb
 52     }
 53     int mid=(l+r)>>1;
 54     if(z<=mid){
 55         update(ls[x],z,add,l,mid);//
 56     }
 57     else update(rs[x],z,add,mid+1,r);//递归儿子啊喂 
 58     pushup(x);
 59 }
 60 int merge(int x,int y,int l,int r){
 61     if(!x||!y){
 62         return x+y;
 63     }
 64     if(l==r){
 65         sum[x]+=sum[y];
 66         return x;
 67     }
 68     int mid=(l+r)>>1;
 69     ls[x]=merge(ls[x],ls[y],l,mid);
 70     rs[x]=merge(rs[x],rs[y],mid+1,r);
 71     pushup(x);
 72     return x;
 73 }
 74 void dfs(int x){
 75     for(int i=first[x];i;i=nex[i]){
 76         int y=to[i];
 77         if(y==f[x][0]) continue;
 78         //root[x]=merge(root[x],root[y],ls[x],rs[x]); my wrong way
 79         dfs(y);
 80         root[x]=merge(root[x],root[y],1,m);
 81     }
 82     if(sum[root[x]])ans[x]=num[posm[root[x]]];//num[posm[root[x]]]  x wai yaojia root
 83 }
 84 int main(){
 85     scanf("%d%d",&n,&m);
 86     t=log2(n);
 87     for(int i=1;i<n;i++){
 88         int a,b;
 89         scanf("%d%d",&a,&b);
 90         add(a,b);
 91         add(b,a);
 92     }
 93     bfs(1);
 94     for(int i=1;i<=m;i++){
 95         scanf("%d%d%d",&x[i],&yy[i],&zz[i]);
 96         num[i]=zz[i];
 97     }
 98     sort(num+1,num+1+m);
 99     for(int i=1;i<=m;i++){
100         zz[i]=lower_bound(num+1,num+m/*m  !n*/+1,zz[i])-num;
101         int lca=Lca(x[i],yy[i]);
102         update(root[x[i]],zz[i],1,1,m);update(root[yy[i]],zz[i],1,1,m);update(root[lca],zz[i],-1,1,m);if(f[lca][0])update(root[f[lca][0]],zz[i],-1,1,m);
103     }
104     dfs(1);
105     for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
106 }

我还是太弱了,orzorz。

 

posted @ 2019-06-18 11:16  Barça_10  阅读(201)  评论(0编辑  收藏  举报