HDU 5057 Argestes and Sequence (离线树状数组 || 分块)

题目链接

题意还是很简单的,Q是查询,S是重新设置数,

一开始用线段树三维数组做的,内存估计有100000*4*10*10,结果超内存了,意料之内,纯粹是想复习一下线段树

后来才学到降维,降低空间牺牲时间,

代码一:
离线树状数组(离线:就是指把操作放一起,每次都要把操作遍历一遍,更新结果数组ans,最后统一把结果输出)


#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <stack>
#include <queue>
#define pi (acos(-1.0))
#define eps (1e-6)
#define inf (1<<28)
#define mod 1000000007
#define MAXN 100010
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
using namespace std;
typedef long long LL;

#define MAX 100100

int lowbit(int t){return t&(-t);}

int getsum(int array[][MAX],int t,int pos)
{
	int s = 0;
	while(t)
	{
		s += array[pos][t];
		t -= lowbit(t);
	}

	return s;
}

void insert(int array[][MAX],int t,int num,int m,int pos)
{
	while(t <= m)
	{
		array[pos][t] += num;
		t += lowbit(t);
	}
}

struct REC
{
    int l,r,pos,dig,index,val;
    char ch;
}rec[MAX];

int T,N,M;
int ans[MAX],a[MAX],a_c[MAX],arr[10][MAX];

int main()
{
    int i,j,pow10;
    cin>>T;
    while(T--){
        cin>>N>>M;
        for(i=0;i<N;i++){
            scanf("%d",&a[i]);
            a_c[i]=a[i];//每次离线操作都会初始化成最开始的状态,预存一个a的副本a_c
        }
        for(i=0;i<M;i++){
            cin>>rec[i].ch;
            if(rec[i].ch=='Q') scanf("%d%d%d%d",&rec[i].l,&rec[i].r,&rec[i].pos,&rec[i].dig);
            else scanf("%d%d",&rec[i].index,&rec[i].val);
        }

        int pow10=1;
        for(i=1;i<=10;i++,pow10*=10){//第i位
            memset(arr,0,sizeof(arr));
            for(j=0;j<N;j++) {
                a[j]=a_c[j];//a恢复初始状态
                insert(arr,j+1,1,N,(a[j]/pow10)%10);//第i位上具体哪个数值
            }
            for(j=0;j<M;j++){
                if(rec[j].ch=='S'){
                    insert(arr,rec[j].index,-1,N,(a[rec[j].index-1]/pow10)%10);//上次的数值减掉
                    insert(arr,rec[j].index,1,N,(rec[j].val/pow10)%10);//这次的数值加上
                    a[rec[j].index-1]=rec[j].val;//记下这次的数值,注意这里a变化了,下次离线求和初始化的时候a需要变成初始状态
                }
                else if(rec[j].pos==i) ans[j]=getsum(arr,rec[j].r,rec[j].dig)-getsum(arr,rec[j].l-1,rec[j].dig);
            }
        }

        for(j=0;j<M;j++)
            if(rec[j].ch=='Q') 
				printf("%d\n",ans[j]);
    }
}

代码二:分块

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <stack>
#include <queue>
#define pi (acos(-1.0))
#define eps (1e-6)
#define inf (1<<28)
#define mod 1000000007
#define MAXN 100010
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
using namespace std;
typedef long long LL;

const int N = 100010;

int block[400][10][10]={0};
int pp[10];
int a[N];
int _,n,m,cnt;

void build()
{
	int i,j;
	
	int tmp = sqrt(n*1.0);
	cnt = n / tmp + 1;
	int id;
	memset(block,0,sizeof(block));

	for(i=0;i<n;i++)
	{
		scanf("%d",&a[i]);
		id = i / cnt;
		tmp = a[i];
		for(j = 0 ;j < 10;j++)
		{
			block[id][j][tmp%10]++;
			tmp /= 10;
		}
	}
}

int query(int l,int r,int dig,int num)
{
	int L = l/cnt,R = r/cnt;
	int i,j,div = pp[dig];
	int res = 0;

	if(L == R)
	{
		for(i=l;i<=r;i++)
			if(a[i]/div%10 == num)
				res++;
		return res;
	}

	for(i = L+1;i<R;i++)
		res += block[i][dig][num];

	for(i = l;i<(L+1)*cnt;i++)
		if(a[i]/div%10 == num)
			res++;

	for(i = R*cnt;i<=r;i++)
		if(a[i]/div%10 == num)
			res++;

	return res;
}

void update(int x,int num)
{
	int i;
	int id = x / cnt;
	for(i=0;i<10;i++)
	{
		block[id][i][a[x]%10]--;
		a[x] /= 10;
	}

	a[x] = num;
	for(i=0;i<10;i++)
	{
		block[id][i][num%10]++;
		num /= 10;
	}
}

void solve()
{
	scanf("%d%d",&n,&m);
	build();

	char str[5];
	int x,num;
	int l,r;

	while(m--)
	{
		scanf("%s",str);
		if(str[0] == 'S')
		{
			scanf("%d%d",&x,&num);
			x--;
			update(x,num);
		}
		else
		{
			scanf("%d%d%d%d",&l,&r,&x,&num);
			l--,r--,x--;
			printf("%d\n",query(l,r,x,num));
		}
	}
}

void init()
{
	int i;
	pp[0] = 1;
	for(i=1;i<10;i++)
		pp[i] = pp[i-1] * 10;
}

int main()
{
	init();
	cin>>_;
	while(_--) solve();
	return 0;
}


代码三(错的)  线段树 留个记录吧

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <stack>
#include <queue>
#define pi (acos(-1.0))
#define eps (1e-6)
#define inf (1<<28)
#define mod 1000000007
#define MAXN 100010
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
using namespace std;
typedef long long LL;

int v[MAXN<<2][20][10]={0};
int nn;
int max_dig,max_nozero;

void push_up(int rt)
{
	int i,j;
	for(i=1;i<=20;i++)
		for(j=0;j<10;j++)
			v[rt][i][j] = v[rt<<1][i][j] + v[rt<<1|1][i][j];
}

void build(int l,int r,int rt)
{
	if(l == r)
	{
		int num,cnt;
		memset(v[rt],0,sizeof(v[rt]));
		scanf("%d",&num);
		int i = 1;
		cnt = 0;
		while(num)
		{
			cnt++;
			v[rt][i++][num%10]++;
			num /= 10;
		}

		if(cnt >= max_dig)
		{
			if(cnt == max_dig)
				max_nozero++;
			else
				max_nozero = 1;
			max_dig = cnt;
		}

		return ;
	}

	int m = (l+r)>>1;
	build(ls);
	build(rs);
	push_up(rt);
}

void update(int x,int num,int l,int r,int rt)
{
	if(l == x && r == x)
	{
		int i = 1,cnt = 0;
		memset(v[rt],0,sizeof(v[rt]));
		while(num)
		{
			cnt++;
			v[rt][i++][num%10]++;
			num /= 10;
		}

		if(cnt >= max_dig)
		{
			if(cnt == max_dig)
				max_nozero++;
			else
				max_nozero = 1;
			max_dig = cnt;
		}

		return ;
	}

	int m = (l+r)>>1;
	if(x <= m)
		update(x,num,ls);
	else
		update(x,num,rs);

	push_up(rt);
}

int query(int L,int R,int dig,int num,int l,int r,int rt)
{
	if(L <= l && R >= r)
		return v[rt][dig][num];

	int res = 0;
	int m = (l+r)>>1;
	if(L <= m)
		res += query(L,R,dig,num,ls);
	if(R > m)
		res += query(L,R,dig,num,rs);

	return res;
}

int main()
{
	int i,j,k,N,flag;
	LL temp;
	int T,t,limit,pos;
	int mm,x,y;
	int L,R,dig,num;
	char vis;

	scanf("%d",&T);
	while(T-- && scanf("%d%d",&nn,&mm))
	{
		max_dig = max_nozero = 0;
		build(1,nn,1);
		scanf("%*c");
		while(mm--)
		{
			scanf("%c",&vis);
			if(vis == 'Q')
			{
				scanf("%d%d%d%d%*c",&L,&R,&dig,&num);
				if(num == 0 && dig >= max_dig)
				{
					if(dig == max_dig)
						printf("%d\n",nn - max_nozero);
					else
						printf("%d\n",nn);
					continue;
				}
				printf("%d\n",query(L,R,dig,num,1,nn,1));
			}
			else
			{
				scanf("%d%d%*c",&L,&num);
				update(L,num,1,nn,1);
			}
		}
	}

	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值