• BZOJ 3091 城市旅行 (LCT)


    3091: 城市旅行

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 2251  Solved: 761
    [Submit][Status][Discuss]

    Description

    Input

    Output

    Sample Input

    4 5
    1 3 2 5
    1 2
    1 3
    2 4
    4 2 4
    1 2 4
    2 3 4
    3 1 4 1
    4 1 4

    Sample Output

    16/3
    6/1

    HINT

    对于所有数据满足 1<=N<=50,000 1<=M<=50,000 1<=Ai<=10^6 1<=D<=100 1<=U,V<=N

    Source

    wyx528命题

    题解:

      给出一个无根树,维护四个操作,link,cut,路径加法,路径期望查询,前三个都是LCT的基本操作(LCT见前面的随笔),主要说第四个;

    对于路径期望,如果两点之间的点数为n,那么很明显是C(n+1,2)=n*(n+1)/2;考虑维护分子:

    如图所示,考虑求每个点的贡献,然后求和;第一个点对答案的贡献为a1*1*n(左端点只能选a1,右端点可以选a1~an),

    第二个点对答案的贡献为a2*2*(n-1)(左端点可以选a1或a2,右端点可以选a2~an)

    于是答案就是a1*1*n+a2*2*(n-1)+...+an*n*1 这个东西显然不能暴力求 需要在LCT上维护 但是直接维护根本维护不了 怎么办呢?

    观察左子树 将左子树合并前后的贡献值作差可得

      我们发现这个3其实就是右子树的size+1

      于是我们们令lsum=1*a1+2*a2+3*a3...+n*an;      rsum=n*a1+(n-1)*a2+...+1*an;

    则:root.ans=lson.ans+lson.lsum*(rson.size+1)  +  rson.ans+rson.rsum*(lson.size+1)  +  root.sum*(lson.size+1)*(rson.size+1) (左子树贡献+右子树的贡献+根节点的贡献);

    考虑对于lsum和rsum的维护,lsum的维护为root.lsum = lson.lsum+root.val*(lson.size+1) + rson.lsum + rson.sum*(lson.size+1);

    对于rsum 的维护类似: root.rsum = rson.rsum + root.val*(son.size+1) + lson.rsum + lson.sum*(rson.size+1);

    当对于root为根的整棵树更新的时候root.ans的维护为:加 val*(1*n+2*(n-1)+3*(n-2)+...+n*1)=val*n*(n+1)*(n+2)/6(这里的n=root.size)。

    1$*$n+2$*$(n-1)+3$*$(n-2)+...+n$*$1

    =(1+2+3+...+n)$*$n-(1$*$2+2$*$3+3$*$4+...+(n-1)$*$n)

    =${n^2*(n+1)over 2}$-(1$^2$+2$^2$+3$^2$+...+(n-1)$^2$+1+2+3+...+(n-1))

    =$n^2*(n+1)over 2$-$n*(n-1)*(2*n-1)over 6$ -$n*(n-1)over 2$

    =$(3*n(n+1)-n*(n-1)*(2*n-1)-3*n*(n-1))over 6$

    =$n*(n^2+3*n+2)over 6​$

    =$n*(n+1)*(n+2)over 6$

     

    参考代码:

      1 /**************************************************************
      2     Problem: 3091
      3     User: SongHL
      4     Language: C++
      5     Result: Accepted
      6     Time:1764 ms
      7     Memory:6464 kb
      8 ****************************************************************/
      9  
     10 //BZOJ 3091
     11 //给出一棵无根树,维护四个操作:link,cut,路径加法,路径期望查询
     12 #include<bits/stdc++.h>
     13 #define LL long long
     14 #define inf 2147483640
     15 #define Pi acos(-1.0)
     16 using namespace std;
     17 const int maxn=50010;
     18 int n,m,fa[maxn];
     19 struct node{
     20     int son[2];
     21     LL sum,tag,rev,ans,val,lsum,rsum,size;
     22     int& operator [] (int x) {return son[x];}
     23 }tree[maxn];
     24 LL gcd(LL a,LL b) {return b==0 ? a : gcd(b,a%b);}
     25 namespace LCT{
     26     void reverse(int k) 
     27     {
     28         swap(tree[k][0],tree[k][1]);
     29         swap(tree[k].lsum,tree[k].rsum);
     30         tree[k].rev^=1;
     31     }
     32      
     33     void add(int k,LL val) 
     34     {
     35         tree[k].val+=val;
     36         tree[k].sum+=tree[k].size*val;
     37         tree[k].tag+=val;
     38         tree[k].ans+=tree[k].size*(tree[k].size+1)*(tree[k].size+2)/6*val;
     39         tree[k].lsum+=tree[k].size*(tree[k].size+1)/2*val;
     40         tree[k].rsum+=tree[k].size*(tree[k].size+1)/2*val;
     41     }
     42     int find(int x) {return fa[x] ? find(fa[x]):x;}
     43      
     44     void pushdown(int k) 
     45     {
     46         if(tree[fa[k]][0]==k || tree[fa[k]][1]==k) pushdown(fa[k]);
     47         int l=tree[k][0],r=tree[k][1];
     48         if(tree[k].rev) 
     49         {
     50             if(l) reverse(l);
     51             if(r) reverse(r);
     52             tree[k].rev^=1;
     53         }
     54         if(tree[k].tag) 
     55         {
     56             if(l) add(l,tree[k].tag);
     57             if(r) add(r,tree[k].tag);
     58             tree[k].tag=0;
     59         }
     60     }
     61     void pushup(int k) 
     62     {
     63         int l=tree[k][0],r=tree[k][1];
     64         tree[k].size=tree[l].size+tree[r].size+1;
     65         tree[k].lsum=tree[l].lsum+(tree[l].size+1)*tree[k].val+tree[r].lsum+tree[r].sum*(tree[l].size+1);
     66         tree[k].rsum=tree[r].rsum+(tree[r].size+1)*tree[k].val+tree[l].rsum+tree[l].sum*(tree[r].size+1);
     67         tree[k].sum=tree[l].sum+tree[r].sum+tree[k].val;
     68         tree[k].ans=tree[l].ans+tree[r].ans+tree[l].lsum*(tree[r].size+1)+tree[r].rsum*(tree[l].size+1)+tree[k].val*(tree[l].size+1)*(tree[r].size+1);
     69     }
     70     void rotate(int x) 
     71     {
     72         int y=fa[x],z=fa[y],l,r;
     73         l=tree[y][1]==x;r=l^1;
     74         if(tree[z][0]==y || tree[z][1]==y) tree[z][tree[z][1]==y]=x;
     75         fa[tree[x][r]]=y;fa[x]=z;fa[y]=x;
     76         tree[y][l]=tree[x][r];tree[x][r]=y;
     77         pushup(y);pushup(x);
     78     }
     79     void splay(int x) 
     80     {
     81         pushdown(x);
     82         while(tree[fa[x]][0]==x || tree[fa[x]][1]==x) 
     83         {
     84             int y=fa[x],z=fa[y];
     85             if(tree[z][0]==y || tree[z][1]==y) 
     86             {
     87                 if(tree[z][0]==y^tree[y][0]==x) rotate(x);
     88                 else rotate(y);
     89             }
     90             rotate(x);
     91         }
     92     }
     93     void access(int x) //访问
     94     {
     95         for(int y=0;x;y=x,x=fa[x]) splay(x),tree[x][1]=y,pushup(x);
     96     }
     97     int findroot(int x)//找根(在真实的树中的)
     98     {
     99         access(x);splay(x);
    100         while(tree[x][0]) pushdown(x),x=tree[x][0];
    101         return x;
    102     }
    103     void makeroot(int x) //换根
    104     {
    105         access(x);splay(x);reverse(x);
    106     }
    107     void split(int x,int y)//提取路径
    108     {
    109         makeroot(x);
    110         access(y);splay(y);
    111     }
    112     void link(int x,int y) 
    113     {
    114         makeroot(x);fa[x]=y;
    115     }
    116     void cut(int x,int y) 
    117     {
    118         makeroot(x);access(y);splay(y);
    119         if(tree[y][0]==x && tree[x][1]==0)
    120             tree[y][0]=0,fa[x]=0,pushup(y);
    121     }
    122     void modify(int x,int y,LL val)//对x~y这段路径操作 
    123     {
    124         makeroot(x);access(y);
    125         splay(y);add(y,val);
    126     }
    127     void query(int x,int y) 
    128     {
    129         split(x,y);
    130         LL A=tree[y].ans,B=tree[y].size*(tree[y].size+1)/2;
    131         LL D=gcd(A,B);
    132         printf("%lld/%lld
    ",A/D,B/D);
    133     }
    134 }
    135 using namespace LCT;
    136   
    137 int main() 
    138 {
    139     scanf("%d%d",&n,&m);
    140     for(int i=1;i<=n;i++) scanf("%lld",&tree[i].val);
    141     for(int u,v,i=1;i<n;i++) 
    142     {
    143         scanf("%d%d",&u,&v);
    144         link(u,v);
    145     }
    146     for(int op,x,y,d,i=1;i<=m;i++) 
    147     {
    148         scanf("%d%d%d",&op,&x,&y);
    149         if(op==1) if(find(x)==find(y)) cut(x,y);
    150         if(op==2) if(find(x)!=find(y)) link(x,y);
    151         if(op==3){ scanf("%d",&d); if(find(x)==find(y)) modify(x,y,d); }
    152         if(op==4) if(find(x)==find(y)) query(x,y); else puts("-1");
    153     }
    154     return 0;
    155 }
    View Code

      

  • 相关阅读:
    Django(十五)模板详解:模板标签、过滤器、模板注释、模板继承、html转义
    Python @函数装饰器及用法
    NPM概述及使用简介
    MVC、MVT简介
    十、Vue:Vuex实现data(){}内数据多个组件间共享
    九、响应式发:rem和less(适配移动端)
    八、Vue-lazyload
    Vue点到子路由,父级,无法高亮问题解决
    七、Vue组件库:Element、Swiper(轮播专用组件)
    六、Vue-Router:基础路由处理、路由提取成单独文件、路由嵌套、路由传参数、路由高亮、html5的history使用
  • 原文地址:https://www.cnblogs.com/csushl/p/9912208.html
Copyright © 2020-2023  润新知