HDU - 5057_Argestes and Sequence思路与题解_树状数组_预处理

思路:

Hint 1 

由于该题内存限制,如果使用保存每一位上的特定的数有多少个的树状数组,即

tree[n][digit][number]  n : 数的下标 digit : 第几位 number : 该digit位上的数是几

。这样会超出内存限制的

这是一个三维的数组,有没有办法减少一维呢?如果减少,哪一维是可能减少的呢?


Hint 2

去掉digit那一维,用 tree[n][number] 来储存信息即可

因为省去了digit那一维度,不妨遍历1-10位,当遍历到第i位时,用tree保存当前数位等于number的数有多少即可


Hint 3

在处理第i位时,首先将未经任何修改的原数组中的数所对应位的信息更新到tree上保存,

在接下来的 1-m ,共m个操作中,询问和修改是穿插的,询问中,有一些不是问第i位信息的,真的有必要一个一个判断某个询问是否在问第i位吗?可不可以在读入询问和操作的时候,将询问第i位的询问下标(下标指的是1-m中的某数)记录下来,待处理该位时直接根据索引,调出这些询问的下标是多少呢?


Hint 4

在处理第i位时,现在虽然可以直接定位,那些针对第i位的询问,可是中途穿插的修改怎么实现呢,难道真的需要遍历该询问前的所有操作,看看哪些是修改操作,然后再进行修改吗?
 


Hint 5

将下标为x的询问前,进行的修改的次数,记录下来。具体来说,当遍历到1-m个操作中的某一个下标为x的操作时,若发现该操作是修改,那么将记录修改次数的数组optimes[i](表示i号操作前有多少次修改)的值加1,i从x+1 取到 m,有什么办法加快这一进程吗?是的,再用树状树组


Hint 6

只需修改的次数,就能完成对应修改,是不是将修改依次保存在一个数组中就可以了呢?其中,数组的下标表示这个第几次修改


Solution

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;

#define lowbit(x) (x&(-x))

const int maxn=100005,maxm=maxn;
int n,m;
int tree[2][maxn][10],a[maxn],b[maxn],optimes[maxm];
//tree[0] : times that the number on a specific digit appear
//tree[1] : count of changes before act i
vector<int> qidx[15];
struct query{
	int l,r,p,ans=-1;
}q[maxm];
struct revise{
	int x,y;
};
vector<revise> op;

void update(int t,int x,int p,int d1)
{
	while(x<=maxn)
	{
		tree[t][x][p]+=d1;
		x+=lowbit(x);
	}
}
int sum(int t,int x,int p)
{
	int res=0;
	while(x>0)
	{
		res+=tree[t][x][p];
		x-=lowbit(x);
	}
	return res;
}

int main()
{
	//freopen("in.txt","r",stdin);
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t;
	cin>>t;
	while(t--)
	{
		cin>>n>>m;
		memset(tree,0,sizeof(tree));
		op.clear();
		for(int i=1;i<=10;i++)
			qidx[i].clear();
		
		for(int i=1;i<=m;i++)
			q[i].ans=-1;
		
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
		}
	
		for(int i=1;i<=m;i++)
		{
			char o;
			cin>>o;
			if(o=='S')
			{
				revise rv;
				cin>>rv.x>>rv.y;
				update(1,i,0,1);
				op.push_back(rv);
			}
			else
			{
				int d;
				cin>>q[i].l>>q[i].r>>d>>q[i].p;
				qidx[d].push_back(i);
			}
		}
		
		for(int i=1;i<=m;i++)
		{
			optimes[i]=sum(1,i,0);
			//printf("optimes[%d]=%d\n",i,optimes[i]);
		}
		
		//printf("##%d\n",optimes[7]);
		//process
		long long div[12]={0,1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
		for(int dig=1;dig<=10;dig++)
		{
			for(int i=1;i<=n;i++)
				for(int j=0;j<=9;j++)
					tree[0][i][j]=0;
			
			for(int i=1;i<=n;i++)
				b[i]=a[i];
			
			
			for(int i=1;i<=n;i++)
			{
				int x;
				x=(b[i]/div[dig])%10;
				update(0,i,x,1);
			}
			//the prob comes here
			int p=0;
			for(int i=0;i<qidx[dig].size();i++)
			{
				//printf("qidx[%d].size()=%d\n",dig,qidx[dig].size());
				for(int& j=p;j<optimes[qidx[dig][i]];j++)
				{
					//printf("!!!");
					int x=op[j].x;
					int y=op[j].y;
					int p=(b[x]/div[dig])%10;
					update(0,x,p,-1);
					p=(y/div[dig])%10;
					update(0,x,p,1);
					b[x]=y;
				}
				int idx=qidx[dig][i];
				q[idx].ans=sum(0,q[idx].r,q[idx].p)-sum(0,q[idx].l-1,q[idx].p);
			}
		}
		for(int i=1;i<=m;i++)
		{
			if(q[i].ans!=-1)
				cout<<q[i].ans<<"\n";
		}
		
	}
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值