Group HDU - 4638(树状数组+离线处理)

There are n men ,every man has an ID(1..n).their ID is unique. Whose ID is i and i-1 are friends, Whose ID is i and i+1 are friends. These n men stand in line. Now we select an interval of men to make some group. K men in a group can create K*K value. The value of an interval is sum of these value of groups.  The people of same group's id must be continuous. Now we chose an interval of men and want to know there should be how many groups so the value of interval is max.
InputFirst line is T indicate the case number. 
For each case first line is n, m(1<=n ,m<=100000) indicate there are n men and m query. 
Then a line have n number indicate the ID of men from left to right. 
Next m line each line has two number L,R(1<=L<=R<=n),mean we want to know the answer of [L,R]. 
OutputFor every query output a number indicate there should be how many group so that the sum of value is max.Sample Input
1
5 2
3 1 2 5 4
1 5
2 4
Sample Output
1
2

题意:t组数据,n,m,n个数,m个查询,每个查询为一个 区间,找这个区间中连续的串;

先用 数状数组 从左到右依次维护一边, 因为维护的时候,前面的影响后面的 线段数,所以在判断区间的线段数时,可以消除区间前面数的影响,这时候就应该 把所查询的 区间按照左端点从小到大排一下序;依次消除 区间前面数的印象;

我们用树状数组 维护线段数,从左到右 一次加入 判断 是不是一个新串,其实树状数组还是判断的位置,来维护线段数,判断当前 a[i] 自己 是不是个新串,主要判断 a[i] - 1和 a[i] + 1, 是不是前面已经判断过,是不是树状数组已经维护过了;

如果a[i]-1 和 a[i] + 1都没有维护过的话,那么 add(i,1) ,当前这个是个新串;

如果 a[i] - 1和a[i] + 1 有一个被维护过了,那么当前自己就可以 直接连上,线段数不变  add(i,0);

如果 a[i] -1 和a[i] + 1 都被维护过了,那么我们要做的就是把两个 线段连成一个线段; add(i,-1);

再消除点时,那么这个点就把当前这个 这条线段 分成了两段,a[i]-1 为一段,a[i]+1 这一部分为一段

 所以add(pos[a[i]-1],1);  add(pos[a[i] + 1],1), 为什么明明 一条线段变成两条线段,为啥增加了2;

因为当我们数状数组 维护的时候,一条线段 记数一定是记到 这条线段的最左边的那个位置上为1;所以去掉个 1 ,加上一2;

若 一个区间[l,r],连一条线段最左边的位置,都不包括的话,那么这条线段一定就不包括了;

自己可以模拟一边

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define Max 100010

int c[Max];  // 数状数组 ; 
int a[Max];  // 存输入的值; 
int pos[Max]; // pos[i] i的位置; 
int vis[Max]; // vis[i] i 这个值是不是已经标记过;为判断 a[i]-1 和 a[i]+1,所在线段是不是已经记过数;
int ans[Max];  // 因为结构体排序,还得对应输出; 
int n,m; 
struct node
{
	int x,y;
	int p;
}stu[Max];

int cmp(node a,node b)
{
	return a.x < b.x;
}

int bit(int x)
{
	return x&-x;
}
void add(int x,int val)
{
	while(x<=n)
	{	c[x] += val;
		x = x + bit(x);
	}
}

int sum(int i)
{
	int res = 0;
	while(i>0)
	{
		res += c[i];
		i -= bit(i);
	}
	return res;
}

int getsum(int l,int r)
{
	return sum(r) - sum(l-1);
}
int main()
{
	int i,j;
	int t;
	scanf("%d",&t);
	while(t--)
	{
		memset(vis,0,sizeof(vis));
		memset(c,0,sizeof(c));
		scanf("%d%d",&n,&m);
		for(i = 1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			pos[a[i]] = i;
		}
		for(i = 0;i<m;i++)
		{
			scanf("%d%d",&stu[i].x,&stu[i].y);
			stu[i].p = i;
		}
		sort(stu,stu+m,cmp);
		
		for(i = 1;i<=n;i++)
		{
			vis[a[i]] = 1;
			if(!vis[a[i]-1]&&!vis[a[i]+1]) // 至今左右都不连续,单独是一个团; 
				add(i,1);
			else if(vis[a[i]-1]&&vis[a[i]+1]) // 把两条直线合并成 1 条; 
				add(i,-1);
			else add(i,0);      // 这条线段 前面一定被记过数了; 
		}
		i = 1;
		int num = 0;
		while(num<m)
		{
			int l = stu[num].x,r = stu[num].y;
			for(;i<l;i++)
			{
				if(a[i]>1) add(pos[a[i]-1],1);  // 当前 a[i] 删除掉,a[i]-1和 a[i]+1 所在位置上都加1;  
				if(a[i]<n) add(pos[a[i]+1],1);
			}
			ans[stu[num].p] = getsum(l,r);
			//printf("p==%d\n",stu[].p);
			num++;
		}	
		for(i = 0;i<m;i++)
		{
			printf("%d\n",ans[i]);
		}
	}
	return 0;
} 


猜你喜欢

转载自blog.csdn.net/obsorb_knowledge/article/details/80156958
今日推荐