数据结构专题

线段树

Can you answer these queries?

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4027

一般线段树区间修改是通过传递lazy标记 但是这题传递lazy标记就不太可行 我们可以发现 最大数据最多也只能sqrt七次(sqrt(1)=1) 意思就是最多七次操作之后值就不变 所以我们直接暴力修改到叶子节点 然后加入区间的所有数全为1之后 (sum[rt]=r-l+1)就直接退出当前递归 然后这题有个很气人的坑点 就是输入的a不一定大于b

/**Today you do things people will not do,
tomorrow you will do things people can not do.**/

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 1e5+10;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
ll sum[maxn<<2];
void PushUp(int rt)
{
    sum[rt]=sum[rt<<1|1]+sum[rt<<1];
}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        scanf("%lld",&sum[rt]);
        return;
    }
    int m = (l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    PushUp(rt);
}

void update(int L,int R,int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]=(ll)(sqrt(sum[rt]));
        return;
    }
    if(sum[rt]==(r-l+1)) return;
    int m=(l+r)>>1;
    if(L<=m) update(L,R,l,m,rt<<1);
    if(m<R) update(L,R,m+1,r,rt<<1|1);
    PushUp(rt);
}
ll query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R)
        return sum[rt];
    int m=(l+r)>>1;
    ll ans=0;
    if(L<=m) ans+=query(L,R,l,m,rt<<1);
    if(m<R) ans+=query(L,R,m+1,r,rt<<1|1);
    return ans;
}
int main()
{
    int n;
    int tot=0;
    while(scanf("%d",&n)==1)
    {
        memset(sum,0,sizeof(sum));
        printf("Case #%d:\n",++tot);
        build(1,n,1);
        ll t;
        scanf("%lld",&t);
        while(t--)
        {
            int num;
            scanf("%d",&num);
            if(num==0)
            {
                int a,b;
                scanf("%d %d",&a,&b);
                if(a>b) swap(a,b);
                update(a,b,1,n,1);
            }
            else
            {
                int a,b;
                scanf("%d %d",&a,&b);
                if(a>b) swap(a,b);
                printf("%lld\n",query(a,b,1,n,1));
            }
        }
        printf("\n");
    }
    return 0;
}

 Tunnel Warfare

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1540

D:摧毁x

R:重建上一个被摧毁的x

Q:查询x左右两边被摧毁的城市中连续的未被摧毁的城市的个数

这一题有两种做法

1.维护两个数组 第一个用于查询右边离x最近的被摧毁的城市 第二个用于查询左边离x最近的被摧毁的城市

我们可以初始化查询max的数组全为n+1 查询min的数组全为0 如果x城市被摧毁 就更新x对应的两个数组成x

然后查询q 查询1~q的最大值 q~n的最小值 ans=minn-maxx-1

/**Today you do things people will not do,
tomorrow you will do things people can not do.**/

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 5e4+10;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
int summin[maxn<<2],summax[maxn<<2];
int rebuild[maxn<<2];
int n,m,tot;

void build(int l,int r,int rt)
{
    if(l==r)
    {
        summin[rt]=n+1;
        summax[rt]=0;
        return;
    }
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    summin[rt]=min(summin[rt<<1|1],summin[rt<<1]);
    summax[rt]=max(summax[rt<<1|1],summax[rt<<1]);
}

void updatemax(int pos,int c,int l,int r,int rt)
{
    if(l==r)
    {
        summax[rt]=c;
        return;
    }
    int m=(l+r)>>1;
    if(pos<=m)
        updatemax(pos,c,l,m,rt<<1);
    if(m<pos)
        updatemax(pos,c,m+1,r,rt<<1|1);
    summax[rt]=max(summax[rt<<1],summax[rt<<1|1]);
}

void updatemin(int pos,int c,int l,int r,int rt)
{
    if(l==r)
    {
        summin[rt]=c;
        return;
    }
    int m=(l+r)>>1;
    if(pos<=m)
        updatemin(pos,c,l,m,rt<<1);
    if(m<pos)
        updatemin(pos,c,m+1,r,rt<<1|1);
    summin[rt]=min(summin[rt<<1],summin[rt<<1|1]);
}

int querymax(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R)
        return summax[rt];
    int m=(l+r)>>1;
    int ans=0;
    if(L<=m)
        ans=max(ans,querymax(L,R,l,m,rt<<1));
    if(m<R)
        ans=max(ans,querymax(L,R,m+1,r,rt<<1|1));
    return ans;
}

int querymin(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R)
        return summin[rt];
    int m=(l+r)>>1;
    int ans=INF;
    if(L<=m)
        ans=min(ans,querymin(L,R,l,m,rt<<1));
    if(m<R)
        ans=min(ans,querymin(L,R,m+1,r,rt<<1|1));
    return ans;
}

int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        tot=0;
        memset(rebuild,0,sizeof(rebuild));
        memset(summin,0,sizeof(summin));
        memset(summax,0,sizeof(summax));
        build(1,n,1);
        while(m--)
        {
            getchar();
            char c;
            scanf("%c",&c);
            if(c=='D')
            {
                int num;
                scanf("%d",&num);
                updatemax(num,num,1,n,1);
                updatemin(num,num,1,n,1);
                rebuild[++tot]=num;
            }
            else if(c=='R')
            {
                int u=rebuild[tot--];
                updatemax(u,0,1,n,1);
                updatemin(u,n+1,1,n,1);
            }
            else if(c=='Q')
            {
                int num;
                scanf("%d",&num);
                int maxx=querymax(1,num,1,n,1);
                int minn=querymin(num,n,1,n,1);
                if(maxx==minn)
                    printf("0\n");
                else
                    printf("%d\n",minn-maxx-1);
            }
        }
    }
}

 2.线段树维护连续子区间 先将村庄抽象成一个点 正常下值为1 被摧毁后变成0 即可将题目抽象成一个求目标节点所在连续1序列的区间的长度 所以可以维护两个数组

一个是从左边开始累加区间长度的数组(lsum[]),一个是从右边开始累加区间长度的数组(rsum[])

(代码有bug)

 

/**Today you do things people will not do,
tomorrow you will do things people can not do.**/

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 5e4+10;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
int lsum[maxn<<2],rsum[maxn<<2],flag;
int tot,rebuild[maxn<<2];

void build(int l,int r,int rt)
{
    if(l==r)
    {
        lsum[rt]=rsum[rt]=1;
        return;
    }
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    lsum[rt]=lsum[rt<<1]+lsum[rt<<1|1];
    rsum[rt]=rsum[rt<<1]+rsum[rt<<1|1];
}

void PushUp(int rt,int m)
{
    lsum[rt]=lsum[rt<<1];
    rsum[rt]=rsum[rt<<1|1];
    if(lsum[rt]==m-(m>>1))
        lsum[rt]+=lsum[rt<<1|1];
    if(rsum[rt]==(m>>1))
        rsum[rt]+=rsum[rt<<1];
}

void update(int pos,int c,int l,int r,int rt)
{
    if(l==r)
    {
        lsum[rt]=rsum[rt]=c;
        return;
    }
    int m=(l+r)>>1;
    if(pos<=m)
        update(pos,c,l,m,rt<<1);
    if(m<pos)
        update(pos,c,m+1,r,rt<<1|1);
    PushUp(rt,r-l+1);
}

int query(int pos,int l,int r,int rt)
{
    if(rsum[rt]>=r-pos+1)
    {
        flag=1;
        return rsum[rt];
    }
    if(lsum[rt]>=pos-l+1)
    {
        flag=1;
        return lsum[rt];
    }
    if(l==r)
        return 0;
    int m=(l+r)>>1;
    int ans;
    if(pos<=m)
    {
        ans=query(pos,l,m,rt<<1);
        if(flag)
            flag=0,ans+=lsum[rt<<1|1];
    }
    if(m<pos)
    {
        ans=query(pos,m+1,r,rt<<1|1);
        if(flag)
            flag=0,ans+=rsum[rt<<1];
    }
    return ans;
}

int main()
{
    int n,m;
    while(~scanf("%d %d",&n,&m))
    {
        memset(lsum,0,sizeof(lsum));
        memset(rsum,0,sizeof(rsum));
        memset(rebuild,0,sizeof(rebuild));
        build(1,n,1);
        tot=0,flag=0;
        while(m--)
        {
            getchar();
            char c;
            scanf("%c",&c);
            if(c=='D')
            {
                int num;
                scanf("%d",&num);
                update(num,0,1,n,1);
                rebuild[++tot]=num;
            }
            if(c=='R')
            {
                int u=rebuild[tot--];
                update(u,1,1,n,1);
            }
            if(c=='Q')
            {
                int num;
                scanf("%d",&num);
                printf("%d\n",query(num,1,n,1));
            }
        }
    }
}

Assign the task

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3974

模板题 但是建树过程不一样 最近才搞懂了为什么要先dfs先序 因为要用线段树存储数据 就必须处理成链式的数据 所以首先要把树形的数据结构转换成链式 然后找入度为0的点作为根节点进行dfs序 然后套线段树模板即可

/**Today you do things people will not do,
tomorrow you will do things people can not do.**/

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cstring>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 5e4+10;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
int s[maxn],e[maxn];
int sum[maxn<<2];
vector<int> G[maxn];
int vis[maxn];
int tot;

void dfs(int rt)
{
    s[rt]=++tot;
    for(int i=0; i<G[rt].size(); i++)
        dfs(G[rt][i]);
    e[rt]=tot;
}

void build(int l,int r,int rt)
{
    sum[rt]=-1;
    if(l==r)
        return;
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
}

void update(int L,int R,int c,int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]=c;
        return;
    }
    int m=(l+r)>>1;
    if(L<=m)
        update(L,R,c,l,m,rt<<1);
    if(m<R)
        update(L,R,c,m+1,r,rt<<1|1);
}

int query(int pos,int l,int r,int rt)
{
    if(l==r)
        return sum[rt];
    int m=(l+r)>>1;
    if(pos<=m)
        return query(pos,l,m,rt<<1);
    if(m<pos)
        return query(pos,m+1,r,rt<<1|1);
}

int main()
{
    int t,k=1;
    scanf("%d",&t);
    while(t--)
    {
        memset(vis,0,sizeof(vis));
        int n;
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
            G[i].clear();
        for(int i=1; i<n; i++)
        {
            int u,v;
            scanf("%d %d",&u,&v);
            G[v].push_back(u);
            vis[u]=1; //标记u为有入度的点
        }
        tot=0;
        for(int i=1; i<=n; i++)
        {
            if(vis[i]==0) //入度为0的点为树根
            {
                dfs(i);
                break;
            }
        }
        build(1,n,1);
        int m;
        scanf("%d",&m);
        printf("Case #%d:\n",k++);
        for(int i=1; i<=m; i++)
        {
            char c[10];
            cin>>c;
            if(c[0]=='C')
            {
                int x;
                scanf("%d",&x);
                printf("%d\n",query(s[x],1,n,1));
            }
            if(c[0]=='T')
            {
                int x,y;
                scanf("%d %d",&x,&y);
                update(s[x],e[x],y,1,n,1);
            }
        }
    }
    return 0;
}

Vases and Flowers

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4614

还没搞懂 先占个坑 以后搞懂了填坑

线段树还没搞懂的知识点:

1.区间合并

2.扫描线

posted @ 2019-08-09 21:30  JCCCCCCCCCCC  阅读(103)  评论(0)    收藏  举报