zoukankan      html  css  js  c++  java
  • 线段树之各类模板

    线段树的认识,可以参考哔哔丽丽的视频。

    (1)单点曽减,区间求和

    飞翔

    #include<stdio.h>
    #define lson l,m,rt<<1///左儿子
    #define rson m+1,r,rt<<1|1///右儿子
    const int maxn = 55555;
    int sum[maxn<<2];
    void PushUP(int rt)///上一个的值由两个儿子相加
    {
        sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    }
    void build(int l,int r,int rt)///建立
    {
        if(l==r)
        {
            scanf("%d",&sum[rt]);///在建立的过程中赋值
            return ;
        }
        int m=(l+r)>>1;
        build(lson);
        build(rson);
        PushUP(rt);
    }
    void update(int p,int add,int l,int r,int rt)
    ///p点增减add
    {
        if(l==r)
        {
           sum[rt]+=add;
           return ;
        }
        int m=(l+r)>>1;
        if(p<=m)
        update(p,add,lson);
        else
        update(p,add,rson);
        PushUP(rt);
    }
    ///LR区间求和
    int query(int L,int R,int l,int r,int rt)
    {
        if(L <= l && r<=R)
            return sum[rt];
        int m=(l+r)>>1;
        int ret=0;
        if(L <= m )
        ret+=query(L,R,lson);
        if(R > m)
        ret+=query(L,R,rson);
        return ret;
    }
    int main( )
    {
        int a,b,t,n;
        scanf("%d",&t);
        for(int cas=1 ; cas<=t ; cas++)
        {
            printf("Case %d:
    ",cas);
            scanf("%d",&n);
            build(1,n,1);
            char op[10];
            while(scanf("%s",op))
            {
                if(op[0]=='E')
                break;
                int a,b;
                scanf("%d%d",&a,&b);
                if(op[0]=='S')
                update(a,-b,1,n,1);
                else if(op[0]=='Q')
                printf("%d
    ",query(a,b,1,n,1));
                else
                update(a,b,1,n,1);
            }
        }
        return 0;
    }
    View Code

    (2)单点替换,区间最值

    飞翔

    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    
    const int manx = 222222;
    int MAX[manx<<2];
    void PushUP(int rt)///父亲的值由儿子来得出
    {
        MAX[rt]=max(MAX[rt<<1],MAX[rt<<1|1]);
    }
    void build(int l,int r,int rt)
    {
        if ( l==r )
        {
            scanf("%d",&MAX[rt]);
            return ;
        }
        int m = (l+r) >> 1;
        build(lson);
        build(rson);
        PushUP(rt);
    }
    void update(int p,int sc,int l,int r,int rt)
    {   
        ///p点变为sc
        if (l==r)
        {
            MAX[rt]=sc;
            return ;
        }
        int m=(l+r)>>1;
        if(p <= m )
        update(p,sc,lson);
        else
        update(p,sc,rson);
        PushUP(rt);
    
    }
    int query(int L,int R,int l,int r,int rt)
    {    ///L到R的最大值
        if(L <= l&&r<= R)
            return MAX[rt];
        int m=(l+r)>>1;
        int ret=0;
        if(L<=m)
        ret=max(ret,query(L,R,lson));
        if(R>m)
        ret=max(ret,query(L,R,rson));
        return ret;
    }
    int main( )
    {
        int n,m;
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            build(1,n,1);
            while(m--)
            {
                char op[2];
                int a,b;
                scanf("%s%d%d",op,&a,&b);
                if(op[0]=='Q')
                printf("%d
    ",query(a,b,1,n,1));
                else
                update(a,b,1,n,1);
            }
        }
        return 0;
    }
    View Code

    (3)线段树功能:update:成段替换 (由于只query一次总区间,所以可以直接输出1结点的信息) ,区间覆盖

    飞翔

    #include <cstdio>
    
    #include <algorithm>
    
    using namespace std;
    
     
    
    #define lson l , m , rt << 1
    
    #define rson m + 1 , r , rt << 1 | 1
    
    const int maxn = 111111;
    
    int h , w , n;
    
    int col[maxn<<2];
    
    int sum[maxn<<2];
    
    void PushUp(int rt) {
    
             sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    
    }
    
    void PushDown(int rt,int m) {
    
             if (col[rt]) {
    
                     col[rt<<1] = col[rt<<1|1] = col[rt];
    
                     sum[rt<<1] = (m - (m >> 1)) * col[rt];
    
                     sum[rt<<1|1] = (m >> 1) * col[rt];
    
                     col[rt] = 0;
    
             }
    
    }
    
    void build(int l,int r,int rt) {
    
             col[rt] = 0;
    
             sum[rt] = 1;
    
             if (l == r) return ;
    
             int m = (l + r) >> 1;
    
             build(lson);
    
             build(rson);
    
             PushUp(rt);
    
    }
    
    void update(int L,int R,int c,int l,int r,int rt) {
    
             if (L <= l && r <= R) {
    
                     col[rt] = c;
    
                     sum[rt] = c * (r - l + 1);
    
                     return ;
    
             }
    
             PushDown(rt , r - l + 1);
    
             int m = (l + r) >> 1;
    
             if (L <= m) update(L , R , c , lson);
    
             if (R > m) update(L , R , c , rson);
    
             PushUp(rt);
    
    }
    
    int main() {
    
             int T , n , m;
    
             scanf("%d",&T);
    
             for (int cas = 1 ; cas <= T ; cas ++) {
    
                     scanf("%d%d",&n,&m);
    
                     build(1 , n , 1);
    
                     while (m --) {
    
                              int a , b , c;
    
                              scanf("%d%d%d",&a,&b,&c);
    
                              update(a , b , c , 1 , n , 1);
    
                     }
    
                     printf("Case %d: The total value of the hook is %d.
    ",cas , sum[1]);
    
             }
    
             return 0;
    
    }
    View Code

     (4)线段树功能:update:成段增减 query:区间求和

    飞翔

    #include<stdio.h>
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define ll long long
    const int manx = 100011;
    ll sum[manx<<2],la[manx<<2];
    void PushUP(int rt)
    {
        sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    }
    void build(int l,int r,int rt)
    {
        la[rt]=0;
        if(l==r)
        {
            scanf("%lld",&sum[rt]);
            return ;
        }
        int m = (l+r) >> 1;
        build(lson);
        build(rson);
        PushUP(rt);
    }
    void PushDown(int rt,int m)
    {
        if(la[rt])
        {
            la[rt<<1]+=la[rt];
            la[rt<<1|1]+=la[rt];
            sum[rt<<1]+=la[rt]*(m-(m>>1));
            sum[rt<<1|1]+=la[rt]*(m>>1);
            la[rt]=0;
        }
    }
    void update(int L,int R,int c,int l,int r,int rt)
    {
        if(L<=l&&r<=R)
        {
            la[rt]+=c;
            sum[rt]+=(ll)c*(r-l+1);
            return ;
        }
        PushDown(rt,r-l+1);
        int m=(l+r)>>1;
        if(L<=m)
        update(L,R,c,lson);
        if(R>m)
        update(L,R,c,rson);
        PushUP(rt);
    }
    ll query(int L,int R,int l,int r,int rt)
    {
        if(L<=l&&r<=R)
        {
            return sum[rt];
        }
        PushDown(rt,r-l+1);
        ll res=0;
        int m=(l+r)>>1;
        if(L<=m)
        res+=query(L,R,lson);
        if(R>m)
        res+=query(L,R,rson);
        return res;
    }
    int main( )
    {
        int n,m,a,b,c;
        scanf("%d%d",&n,&m);
        build(1,n,1);
        while(m--)
        {
            getchar();
            char op[2];
            scanf("%S",&op);
            if(op[0]=='C')
            {
                scanf("%d%d%d",&a,&b,&c);
                update(a,b,c,1,n,1);
            }
            else
            {
                scanf("%d%d",&a,&b);
                ll t;
                t=query(a,b,1,n,1);
                printf("%lld
    ",t);
            }
    
        }
    }
    View Code

     (5)成段覆盖,单点最大小

    飞翔

    #include<bits/stdc++.h>
    
    using namespace std ;
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    const int maxn = 200020;
    int Begin[maxn << 2], End[maxn << 2];
    int be[maxn],en[maxn],w[maxn];
    long long sum[maxn],d[maxn];
    void pushdown(int rt)//向下跟新
    {
        if(!Begin[rt<<1])
        Begin[rt<<1]=Begin[rt];
        if(!Begin[rt<<1|1])
        Begin[rt<<1|1]=Begin[rt];
    
        if(!End[rt])
        return ;
        End[rt<<1]=End[rt<<1|1]=End[rt];
        End[rt]=0;///优化用过了就可以不用了
    
    }
    void build(int l , int r , int rt)
    {
        Begin[rt]=End[rt]=0;
        if(l==r)
        return ;
        int m = (l+r) >> 1 ;
        build(lson);
        build(rson);
    }
    
    void update(int L , int R , int k , int l , int r , int rt)
    {
        if(L<=l && r<=R)
        {
            if(!Begin[rt])///很简单的道理,我跟新过了就不跟新了;
            Begin[rt]=k;
            End[rt]=k;
            return ;
        }
        pushdown(rt);
        int m=(l+r) >> 1;
        if(m>=L)
        update(L,R,k,lson);
        if(m<R)
        update(L,R,k,rson);
    }
    void pushall(int l , int r , int rt)
    {
        if(l==r)
        {
            be[l]=Begin[rt],en[l]=End[rt];
            return ;
        }
        pushdown(rt);
        int m=(l+r)>>1;
        pushall(lson);
        pushall(rson);
    }
    int main()
    {
        int n,m;
        while(~scanf("%d%d",&n,&m))
        {
            n--;
            build(1,n,1);///建树
            for(int i=1 ; i<=n ; i++)
            scanf("%d",&w[i]);
    
            for(int i=1 ; i<=m ; i++)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                if(u>v)//防止意外
                swap(u,v);
                update(u,v-1,i,1,n,1);///u到v区间更新为i(天);
            }
            pushall(1,n,1);//找到每一段路的开始时间与结束时间
            memset(d,0,sizeof(d));
            for(int i=1 ; i<=n ; i++)//每一段路的开始费用与结束费用
            {
                if(be[i])
                {
                    d[be[i]]+=w[i];
                    d[en[i]+1]-=w[i];
                }
            }
            sum[0]=0;
            for(int i=1 ; i<=m ; i++)///类似与扫描线,一天一天的扫过去
            {
                sum[i]=sum[i-1]+d[i];
                printf("%lld
    ",sum[i]);
            }
        }
    }
    View Code

     (6)

    最大值(区间修改)
    总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 65536kB
    描述
    在N(1<=n<=100000)个数A1…An组成的序列上进行M(1<=m<=100000)次操作,操作有两种:
    (1)1 LR C:表示把A[L]到A[R]增加C(C的绝对值不超过10000);
    (2)2 LR:询问A[L]到A[R]之间的最大值。

    输入
    第一行输入N(1<=N<=100000),表示序列的长度,接下来N行输入原始序列;接下来一行输入M(1<=M<=100000)表示操作的次数,接下来M行,每行为1 L R C或2 L R

    输出
    对于每个操作(2)输出对应的答案。

    #include <cstdio>
    #include <cstdlib>
    #include <cmath>
    #include <cstring>
    #include <string>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <stack>
    #include <queue>
    #include <deque>
    #include <map>
    #include <set>
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int maxn=100005;
    int tree[maxn*3];
    int lazy[maxn*3];
    int n,m;
    
    void build(int node=1, int l=1, int r=n)
    {
        if(l==r)
        {
            scanf("%d",&tree[node]);
            return;
        }
        int mid=(l+r)/2;
        build(node<<1, l, mid);
        build((node<<1)+1, mid+1, r);
        tree[node]=std::max(tree[node<<1], tree[(node<<1)+1]);
    }
    
    void pushdown(int node)
    {
        if(lazy[node])
        {
            lazy[node<<1]+=lazy[node];
            lazy[(node<<1)+1]+=lazy[node];
            tree[node<<1]+=lazy[node];
            tree[(node<<1)+1]+=lazy[node];
            lazy[node]=0;
        }
    }
    
    int g_L,g_R,g_Add;
    void change(int node=1, int l=1, int r=n)
    {
        if(g_L<=l && r<=g_R)
        {
            tree[node]+=g_Add; //这个结点对应线段的所有点都加上了g_Add,所以最大值也加g_Add 
            lazy[node]+=g_Add; //我们只操作这个结点,而不递归传下去,因为这时我们传下去了也用不到,所以通过lazy保存结点对应线段每个点的增加值 
            return;
        }
        int mid=(l+r)/2;
        int lc=node<<1;
        int rc=(node<<1)+1;
        //现在要更新子结点了对吧,既然子结点的最大值还没有加上g_Add,那我们怎么知道加了后的值是多少呢? 
        pushdown(node); //那就更新它,把lazy记号推下去 
        if(g_L<=mid)
            change(lc, l, mid);
        if(g_R>mid)
            change(rc, mid+1, r);
        tree[node]=std::max(tree[lc],tree[rc]); //记住要回来更新父结点 
    }
    
    //使用g_L和g_R
    int query(int node=1, int l=1, int r=n)
    {
        if(g_L<=l && r<=g_R)
        {
            return tree[node]; //注意tree[node]的含义:我们已经保证tree[node]已经更新,所以答案就是tree[node],不要再加上lazy[node],它是作用于子结点的 
        }
        int mid=(l+r)/2;
        int lc=node<<1;
        int rc=(node<<1)+1;
        pushdown(node); //查询时也要更新,以把加上的值记录在内 
    
        int ans=0x80000000;
        if(g_L<=mid)
            ans=std::max(ans, query(lc, l, mid));
        if(g_R>mid)
            ans=std::max(ans, query(rc, mid+1, r));
        return ans;
    }
    
    int main()
    {
        scanf("%d",&n);
        build();
        scanf("%d",&m);
        while(m--)
        {
            int operation, l, r, value;
            scanf("%d%d%d", &operation, &l, &r);
    
            if(operation==1)
            {
                scanf("%d", &value);
                g_L=l;
                g_R=r;
                g_Add=value;
                change();
            }
            else if(operation==2)
            {
                g_L=l;
                g_R=r;
                printf("%d
    ",query());
            }
        }
        return 0;
    }
    View Code

    (7)区间修改的总模板

    <pre name="code" class="cpp">#include <iostream>
    #include <string.h>
    #include <algorithm>
    #include <stdio.h>
    #include <math.h>
    #include <queue>
    #define MAXN 100010
    #define inf 0x3f3f3f3f
     
    using namespace std;
     
    struct node{
        int l,r;//区间[l,r]
        int add;//区间的延时标记
        int sum;//区间和
        int mx; //区间最大值
        int mn; //区间最小值
    }tree[MAXN<<2];//一定要开到4倍多的空间
     
    void pushup(int index){
        tree[index].sum = tree[index<<1].sum+tree[index<<1|1].sum;
        tree[index].mx = max(tree[index<<1].mx,tree[index<<1|1].mx);
        tree[index].mn = min(tree[index<<1].mn,tree[index<<1|1].mn);
    }
    void pushdown(int index){
        //说明该区间之前更新过
        //要想更新该区间下面的子区间,就要把上次更新该区间的值向下更新
        if(tree[index].add > 0){
            //替换原来的值
            /*
            tree[index<<1].sum = (tree[index<<1].r-tree[index<<1].l+1)*tree[index].add;
            tree[index<<1|1].sum = (tree[index<<1|1].r-tree[index<<1|1].l+1)*tree[index].add;
            tree[index<<1].mx = tree[index].add;
            tree[index<<1|1].mx = tree[index].add;
            tree[index<<1].mn = tree[index].add;
            tree[index<<1|1].mn = tree[index].add;
            tree[index<<1].add = tree[index].add;
            tree[index<<1|1].add = tree[index].add;
            tree[index].add = 0;*/
            //在原来的值的基础上加上val
            
            tree[index<<1].sum += (tree[index<<1].r-tree[index<<1].l+1)*tree[index].add;
            tree[index<<1|1].sum +=(tree[index<<1|1].r-tree[index<<1|1].l+1)*tree[index].add;
            tree[index<<1].mx += tree[index].add;
            tree[index<<1|1].mx += tree[index].add;
            tree[index<<1].mn += tree[index].add;
            tree[index<<1|1].mn += tree[index].add;
            tree[index<<1].add += tree[index].add;
            tree[index<<1|1].add += tree[index].add;
            tree[index].add = 0;
     
        }
    }
    void build(int l,int r,int index){
        tree[index].l = l;
        tree[index].r = r;
        tree[index].add = 0;//刚开始一定要清0
        if(l == r){
            scanf("%d",&tree[index].sum);
            tree[index].mn = tree[index].mx = tree[index].sum;
            return ;
        }
        int mid = (l+r)>>1;
        build(l,mid,index<<1);
        build(mid+1,r,index<<1|1);
        pushup(index);
    }
    void updata(int l,int r,int index,int val){
        if(l <= tree[index].l && r >= tree[index].r){
            /*把原来的值替换成val,因为该区间有tree[index].r-tree[index].l+1
            个数,所以区间和 以及 最值为:
            */
            /*tree[index].sum = (tree[index].r-tree[index].l+1)*val;
            tree[index].mn = val;
            tree[index].mx = val;
            tree[index].add = val;//延时标记*/
            //在原来的值的基础上加上val,因为该区间有tree[index].r-tree[index].l+1
            //个数,所以区间和 以及 最值为:
            tree[index].sum += (tree[index].r-tree[index].l+1)*val;
            tree[index].mn += val;
            tree[index].mx += val;
            tree[index].add += val;//延时标记
     
            return ;
        }
        pushdown(index);
        int mid = (tree[index].l+tree[index].r)>>1;
        if(l <= mid){
            updata(l,r,index<<1,val);
        }
        if(r > mid){
            updata(l,r,index<<1|1,val);
        }
        pushup(index);
    }
    int query(int l,int r,int index){
        if(l <= tree[index].l && r >= tree[index].r){
            //return tree[index].sum;
            return tree[index].mx;
            //return tree[index].mn;
        }
        pushdown(index);
        int mid = (tree[index].l+tree[index].r)>>1;
        int ans = 0;
        int Max = 0;
        int Min = inf;
        if(l <= mid){
            ans += query(l,r,index<<1);
            Max = max(query(l,r,index<<1),Max);
            Min = min(query(l,r,index<<1),Min);
        }
        if(r > mid){
            ans += query(l,r,index<<1|1);
            Max = max(query(l,r,index<<1|1),Max);
            Min = min(query(l,r,index<<1|1),Min);
        }
        //return ans;
        return Max;
        //return Min;
    }
    int main()
    {
        int n,m,q,x,y,z;
        while(~scanf("%d%d",&n,&m)){
            build(1,n,1);
            while(m--){
                scanf("%d",&q);
                if(q == 1){
                    cout<<"查询:(x,y)"<<endl;
                    scanf("%d %d",&x,&y);
                    cout<<query(x,y,1)<<endl;
                }
                else{
                    cout<<"更新(x,y)为z:"<<endl;
                    scanf("%d %d %d",&x,&y,&z);
                    updata(x,y,1,z);
                    for(int i = 1; i <= n; ++i){
                        printf("a[%d] = %d
    ",i,query(i,i,1));
                    }
                }
            }
        }
        return 0;
    }
    View Code

     (8)区间修改区间整除

    题意

    初始a数组为0,给你一个全排列的b数组,q次询问add x y为a数组区间x y增加1,query x y查询a数组整除b数组对应下标的和

    题解

    区间操作很容易想到线段树

    初始每个叶子节点赋值为b[i],维护一个区间最小值min,和区间和sum

    对于每个add,区间[X,Y]最小值减1,如果当前区间最小值=1,就继续往下更新,如果更新到叶子节点并且min=1,sum+1

    对于每个query,查询区间[X,Y]sum,如果区间min=0,再去暴力更新区间(可以知道一共q次询问,q/1+q/2+q/3+....q/n为调和级数,复杂度O(logn))

    总复杂度O(nlog^2 n)

        这个题一眼望过去,区间更新,区间求和,很明显就可以想到用线段树进行维护。

        但是这个问题的难点在于这个式子 c[i]=ai/bi的求和相对来说不好直接处理。在这里,我们需要对这个式子进行分析。我们需要发现,对于每一个c[i],它的值倘若能够成功加1,那么必定是ai在区间更新中增加了bi 。因此我们就可以往这个方向去考虑。

        但是对ai不断的加1并判断与bi的关系,处理起来相对比较麻烦,因此我们就可以逆向考虑:使得在每次更新中bi-1,而若bi减到0,则使答案+1,并使得bi重新变为原值。

        如此一来,我们只需要用线段树对区间bi的最小值min以及答案c[i]进行维护即可

    #include <bits/stdc++.h>
    #define maxn 100005
    using namespace std;
    typedef long long ll;
    struct Tree{
        ll sum,valb,minn,add;
    }tr[maxn<<2];
    int n,q;
    int a[maxn],b[maxn];
    void push_up(int rt){//维护区间bi的最小值以及答案
        tr[rt].minn=min(tr[rt<<1].minn,tr[rt<<1|1].minn);
        tr[rt].sum=tr[rt<<1].sum+tr[rt<<1|1].sum;
    }
    void push_down(int rt){//lazy操作
        if(tr[rt].add){
            tr[rt<<1].add+=tr[rt].add;
            tr[rt<<1|1].add+=tr[rt].add;
            tr[rt<<1].minn-=tr[rt].add;
            tr[rt<<1|1].minn-=tr[rt].add;
            tr[rt].add=0;
        }
    }
    void build(int l,int r,int rt){//建树
        tr[rt].add=tr[rt].sum=0;
        if(l==r){
            tr[rt].minn=tr[rt].valb=b[l];
            tr[rt].sum=0;
            return;
        }
        int mid=(l+r)>>1;
        build(l,mid,rt<<1);
        build(mid+1,r,rt<<1|1);
        push_up(rt);
    }
    void update(int L,int R,int l,int r,int rt){//区间更新
        //如果询问区间[L,R]能被覆盖且[L,R]的最小值大于0,则进行-1操作
        if(L<=l&&R>=r&&tr[rt].minn>1){
            tr[rt].add++;
            tr[rt].minn--;
            return;
        }
        //否则不断遍历到叶子结点并判断当前bi的值是否为0
        if(l==r&&tr[rt].minn==1){
            tr[rt].sum++;
            tr[rt].minn=tr[rt].valb;
            tr[rt].add=0;
            return;
        }
        int mid=(l+r)>>1;
        push_down(rt);
        if(L<=mid) update(L,R,l,mid,rt<<1);
        if(R>mid) update(L,R,mid+1,r,rt<<1|1);
        push_up(rt);
    }
    ll query(int L,int R,int l,int r,int rt){//区间求和
        if(L<=l&&R>=r){
            return tr[rt].sum;
        }
        int mid=(l+r)>>1;
        push_down(rt);
        ll ans=0;
        if(L<=mid) ans+=query(L,R,l,mid,rt<<1);
        if(R>mid) ans+=query(L,R,mid+1,r,rt<<1|1);
        return ans;
    }
    int main()
    {
        while(~scanf("%d%d",&n,&q)){
            for(int i=1;i<=n;i++){
                scanf("%d",&b[i]);
            }
            build(1,n,1);
            while(q--){
                string str;
                cin>>str;
                int l,r;
                scanf("%d%d",&l,&r);
                if(str[0]=='a'){
                    update(l,r,1,n,1);
                }
                else{
                    ll res=query(l,r,1,n,1);
                    printf("%lld
    ",res);
                }
            }
        }
        return 0;
    }
    View Code

    感激这位大牛的模板

  • 相关阅读:
    Java基础回顾 —内部类
    JAVA设计模式之观察者模式
    Java基础回顾 —注解
    JAVA设计模式之代理模式
    Java基础回顾 —反射机制
    JAVA设计模式之策略模式
    Java基础回顾 —String常量池
    JAVA设计模式之单例模式
    Java基础回顾 —抽象类、接口、多态
    Git的简单应用
  • 原文地址:https://www.cnblogs.com/shuaihui520/p/9069021.html
Copyright © 2011-2022 走看看