题目来自大神博客的线段树专题

http://www.notonlysuccess.com/index.php/segment-tree-complete/

 

hdu1166 敌兵布阵
题意:O(-1)
思路:O(-1)
线段树功能:update:单点增减 query:区间求和

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <utility>
#include <queue>
#include <stack>
using namespace std;
const int INF=1<<30;
const double eps=1e-6;
const int N = 50010;
int sum[N<<2],n,M;

void add(int x,int v)
{
    for(x+=M;x;x>>=1)
        sum[x]+=v;
}
int query(int l,int r)
{
    int res=0;
    for(l=l+M-1,r=r+M+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1) res+=sum[l^1];
        if(r&1) res+=sum[r^1];
    }
    return res;
}
void run()
{
    scanf("%d",&n);
    for(M=1;M<=n+1;M*=2);
    memset(sum,0,sizeof(sum));
    int x,y;
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&x);
        add(i,x);
    }
    char s[15];
    static int cas=1;
    printf("Case %d:\n",cas++);
    while(scanf("%s",s)!=EOF && s[0]!='E')
    {
        scanf("%d%d",&x,&y);
        if(s[0]=='Q')   printf("%d\n",query(x,y));
        else if(s[0]=='A') add(x,y);
        else  add(x,-y);
    }
}

int main()
{
//    freopen("case.txt","r",stdin);
    int _;
    scanf("%d",&_);
    while(_--)
        run();
    return 0;
}
View Code

PS: 第一次交RE了一次,原因是,题目的n范围是5*10^5,我只开了数组比这个数的两倍大一点。但是zkw要求范围是2的k次幂那样的,所以开数组要开大点吧

 

hdu1754 I Hate It
题意:O(-1)
思路:O(-1)
线段树功能:update:单点替换 query:区间最值

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <utility>
#include <queue>
#include <stack>
using namespace std;
const int INF=1<<30;
const double eps=1e-6;
const int N = 200000;

int mes[N<<2],n,m,M;

void update(int x,int v)
{
    x+=M;mes[x]=v;
    for(x>>=1;x;x>>=1)
        mes[x]=max(mes[x<<1],mes[x<<1|1]);
}
int query(int l,int r)
{
    int res=0;
    for(l=l+M-1,r=r+M+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1) res=max(res,mes[l^1]);
        if(r&1) res=max(res,mes[r^1]);
    }
    return res;
}

void run()
{
    for(M=1;M<=n+1;M<<=1);
    memset(mes,0,sizeof(mes));
    int x,y;
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&x);
        update(i,x);
    }
    char s[5];
    while(m--)
    {
        scanf("%s%d%d",s,&x,&y);
        if(s[0]=='Q')   printf("%d\n",query(x,y));
        else update(x,y);
    }
}

int main()
{
//    freopen("case.txt","r",stdin);
    while(scanf("%d%d",&n,&m)!=EOF)
        run();
    return 0;
}
View Code

顺利1Y

 

hdu1394 Minimum Inversion Number
题意:求Inversion后的最小逆序数
思路:用O(nlogn)复杂度求出最初逆序数后,就可以用O(1)的复杂度分别递推出其他解。线段树是用在求最初逆序数上。递推方法详细在下面
线段树功能:update:单点增减 query:区间求和

递推: 因为题目总是把第一个数移到最后一个位置,所以原来比它小的数(和它构成逆序)在移动之后就不是逆序了,而原来比它大的数(不和它构成逆序)在移动之后就是逆序了,这样sum就变化了: Sum=sum-(low[a[i]])+(up[a[i]]);   显然在序列0,1,2,…..n-1中  比a[i]小的数的个数是 Low[a[i]]=a[i];  比a[i]大的数的个数是 up[a[i]]=n-a[i]-1;   题目要求是循环移动n次,那么只要写个for,把a[0],a[1],a[2]……a[n-1]都移动一遍,sum进行n次上面的公式运算,同时记录最小值,就是最小逆序数了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <utility>
#include <queue>
#include <stack>
using namespace std;
const int INF=1<<30;
const double eps=1e-6;
const int N = 5010;
int sum[N<<2],n,M;
int a[N];

inline void add(int x)
{
    for(x+=M;x;x>>=1)
        ++sum[x];
}
inline int query(int l,int r)
{
    int res=0;
    for(l=l+M-1,r=r+M+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1) res+=sum[l^1];
        if(r&1) res+=sum[r^1];
    }
    return res;
}

void run()
{
    int s=0;
    memset(sum,0,sizeof(sum));
    for(M=1;M<=n+1;M<<=1);
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&a[i]);
        ++a[i];//注意后面add的时候不能在0位置上add
        if(a[i]!=n)
            s+=query(a[i]+1,n);
        add(a[i]);
    }
    int ans=s;
    for(int i=1;i<=n;++i)
    {
        s = s + (n - a[i]) - (a[i] - 1);
        if(s<ans)   ans=s;
    }
    printf("%d\n",ans);
}

int main()
{
//    freopen("case.txt","r",stdin);
    while(scanf("%d",&n)!=EOF)
        run();
    return 0;
}
View Code

一个要注意的地方是zkw不能在0位置操作,所以应该把a[i]都加一

 

hdu2795 Billboard
题意:h*w的木板,放进一些1*L的物品,求每次放空间能容纳且最上边的位子
思路:每次找到最大值的位子,然后减去L
线段树功能:query:区间求最大值的位子(直接把update的操作在query里做了)

跪了这个问题暂时没想到zkw怎么搞,这个是要根据当前区间最值来判断是去左子树还是右子树找,需要自顶向下的递归

 

 

//---------其他题目---------//

hdu4366 

详细题解: hdu4366

需要用到线段树

单点更新,区间最值

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
const int N = 50010;

struct _edge{
    int to,next;
};
_edge edge[N*2];
int ecnt,head[N];
void addedge(int u,int v)
{
    edge[ecnt].to = v;
    edge[ecnt].next = head[u];
    head[u] = ecnt++;
}
struct node{
    int id,a,b,l,r;
    friend bool operator < (const node &a, const node &b)
    {
        return a.a>b.a;
    }
};

int n,m,M;
int zkw[N*10][2];
node man[N];
int ans[N];
int dfscnt;
void dfs(int u,int fa)
{
    man[u].l = dfscnt++;
    for(int e=head[u];e!=-1;e=edge[e].next)
    {
        int &v = edge[e].to;
        if(v==fa) continue;
        dfs(v,u);
    }
    man[u].r = dfscnt++;
}

void add(int x,int a,int b)
{
    for(x+=M;x;x>>=1)
        if(zkw[x][0]<a)
            zkw[x][0]=a,zkw[x][1]=b;
}
int query(int l,int r)
{
    int a,b;
    a=b=-1;
    for(l=l+M-1,r=r+M+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1 && zkw[l^1][0]>a) a=zkw[l^1][0],b=zkw[l^1][1];
        if(r&1 && zkw[r^1][0]>a) a=zkw[r^1][0],b=zkw[r^1][1];
    }
    return b;
}

void run()
{
    scanf("%d%d",&n,&m);
    memset(head,-1,sizeof(head));
    ecnt=0;
    int a,b,c;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        addedge(a,i);
        addedge(i,a);
        man[i].a=c;
        man[i].b=b;
        man[i].id=i;
    }
    dfscnt=1;
    dfs(0,-1);
//    for(int i=0;i<n;i++)
//        printf("%d %d %d\n",i,man[i].l,man[i].r);
    sort(man+1,man+n);

    for(M=1;M<=dfscnt+1;M*=2);
    memset(zkw,-1,sizeof(zkw));

    stack<int> stk;
    stk.push(1);
    ans[man[1].id]=-1;
    for(int i=2;i<n;i++)
    {
        if(man[i].a!=man[i-1].a)
        {
            while(!stk.empty())
            {
                int u = stk.top(); stk.pop();
                add(man[u].l,man[u].b,man[u].id);
                add(man[u].r,man[u].b,man[u].id);
            }
        }
        stk.push(i);
        ans[man[i].id] = query(man[i].l,man[i].r);
    }
    while(m--)
    {
        scanf("%d",&a);
        printf("%d\n",ans[a]);
    }
}

int main()
{
    freopen("case.txt","r",stdin);
    int _;
    scanf("%d",&_);
    while(_--)
        run();
    return 0;
}
View Code

 

 posted on 2014-08-18 00:28  someblue  阅读(424)  评论(0编辑  收藏  举报