c语言 操作格子

来源:互联网 发布:钢铁生产过程知乎 编辑:程序博客网 时间:2024/05/01 04:09
算法训练 操作格子  
时间限制:1.0s   内存限制:256.0MB
      
问题描述
有n个格子,从左到右放成一排,编号为1-n。
共有m次操作,有3种操作类型:
1.修改一个格子的权值,
2.求连续一段格子权值和,
3.求连续一段格子的最大值。
对于每个2、3操作输出你所求出的结果。
输入格式
第一行2个整数n,m。
接下来一行n个整数表示n个格子的初始权值。
接下来m行,每行3个整数p,x,y,p表示操作类型,p=1时表示修改格子x的权值为y,p=2时表示求区间[x,y]内格子权值和,p=3时表示求区间[x,y]内格子最大的权值。
输出格式
有若干行,行数等于p=2或3的操作总数。
每行1个整数,对应了每个p=2或3操作的结果。
样例输入
4 3
1 2 3 4
2 1 3
1 4 3
3 1 4
样例输出
6
3
数据规模与约定
对于20%的数据n <= 100,m <= 200。
对于50%的数据n <= 5000,m <= 5000。

对于100%的数据1 <= n <= 100000,m <= 100000,0 <= 格子权值 <= 10000。




画图什么的不太会,,不过题目提示用线段树,,,据我查阅资料,,得知就是满二叉树,,,

对题目的要求,,,可以将无用的点设为0即可


#include <stdio.h>
#include <string.h>
#define MAX 1<<18
int dat[MAX][4];
int n,m;
int max(int a, int b)
{
    return a>b?a:b;
}
int tty()//n个节点建立的最低满二叉树的最后一层的节点数
{
    int i=1;
    while(i<n)
        i *= 2;
    return i;
}
void init()
{
    int i = tty()-1;//第一个需要更新的节点编号
    int k,j;
    while(i >= 1)
    {
        j = i*2;//左海子
        k = i*2+1;//右孩子
        dat[i][0] = max(dat[j][0],dat[k][0]);//最大值
        dat[i][1] = dat[j][1]+dat[k][1];//总和
        dat[i][2] = dat[j][2];//左区间
        dat[i][3] = dat[k][3];//右区间
        i--;
    }
}
void update(int a, int b)
{
    int i=tty()+a-1;//更新的位置
    int j;
    dat[i][1] = dat[i][0] = b;
    while(i > 1)
    {
        j = i/2;//更新的父亲节点
        dat[j][0] = max(dat[j*2][0],dat[j*2+1][0]);
        dat[j][1] = dat[j*2][1]+dat[j*2+1][1];
        i = j;
    }
}
int llsum(int a,int b, int c)
{
    if(b==dat[a][2] && c==dat[a][3])
        return dat[a][1];
    int middle = (dat[a][2]+dat[a][3])/2;
    if(c <= middle)
        return llsum(a*2,b,c);
    else if(b > middle)
        return llsum(a*2+1,b,c);
    else
        return llsum(a*2,b,middle)+llsum(a*2+1,middle+1,c);
}
int query(int a,int b, int c)
{
    if(b==dat[a][2] && c==dat[a][3])
        return dat[a][0];
    int middle = (dat[a][2]+dat[a][3])/2;
    if(c <= middle)
        return query(a*2,b,c);
    else if(b > middle)
        return query(a*2+1,b,c);
    else
        return max(query(a*2,b,middle),query(a*2+1,middle+1,c));
}
int main()
{
    int i,j,k,a,b,c,q;
    memset(dat,0,sizeof(dat));
    scanf("%d%d",&n,&m);
    i = tty();//起始元素的位置
    j = i*2-1;//最后一个元素的位置
    k = 1;//元素的区间编号
    q = i+n-1;//结束元素的位置
    for( ; i<=q; i++)
    {
        scanf("%d",&dat[i][0]);
        dat[i][1] = dat[i][0];
    }
    j = i = tty();//起始元素的位置
    //j = j/2+1;//最后一层进行区间编号的个数
    while(k <= j)
    {
        dat[i][2] = dat[i][3] = k;
        k++;
        i++;
    }
    j = (tty()-1)*2;//最后一个元素的位置
    init();//初始化,建立线段树
    for(i=1; i<=m; i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        if(a == 1)//更新
            update(b,c);
        else if (a == 2)//区间和
            printf("%d\n",llsum(1,b,c));
        else//b、c区间的最大值
            printf("%d\n",query(1,b,c));
    }
    return 0;
}


感觉不是很成熟,,,希望大家多多指点,帮助改进

0 0