HDU 4638 Group (莫队算法)

Group

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2377    Accepted Submission(s): 1219


Problem Description

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.
 
Input

First 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].
 
Output

For 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

        题目大意:把对应询问区间内的数字分成很多个组,要求每个组内的数字的id必须是连续的。假设某个组的数字数为k,则该组的价值是k*k,让你输出总价值最大的分法。
        其实根本不用怎么管什么小组的价值,很容易发现,组数越少,总价值必然最大。那么问题就转化为了,把某段区间内所有数分组,问最少组数是多少。自然而然的,我们想到了区间万能算法——莫队算法。
        对于每一个id,我们设置一个布尔型的num数组,若num[i]==1,则表示id为i的数字在当前区间中出现过,反之则未出现。然后,每一次转移的时候,把新加进来的数字的id的num标记为1,然后判断该id前后的两个数字是否出现过。若前后两个数字都出现过,那么当前数字可以当作桥梁连接前后两个数字,使得三个数字在同一个分组内,故总小组数目减少一。若前后两个数字都没有出现,那么当前数字只能独自成一组,总小组数目加一。若前后两个数字只出现了一个,那么当前数字就与出现的那个分在同一组,总小组数不变。同理,删除的时候,把其num标记为0,也判断其前后两个数字的出现情况,对应做法与添加的时候完全相反。
        其实,这样的做法看起来确实挺神奇的,通过一个num数组很好的保存了前后数字的存在情况。但如果你就这样写完然后自信满满地交上去的,那么就呵呵了……
        我们要考虑一个特殊情况,如果区间转移的时候,前后两个区间完全没有重合会发生什么情况呢?左端点的移动将会导致删去一些本不存在的点或者添加一些已经添加的点,右端点也是如此。按照以往的经验,添加删除可以抵消,但是在这里可以吗?显然,由于我们在统计ans的时候,要根据前后两个数字来决定增减,这样的话删除一个本不存在的数然后再添加回去和添加一个已经存在的数再删除它的效果并不会相互抵消,因而就会产生错误。所以,在遇到这样的情况之后,我们要进行特殊的处理,先把之前区间的所有数字删除,并对ans清零,然后再对应当前区间求结果。
        这要操作之后,我们就能避免错误。代码如下:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAX_V 100100
using namespace std;

struct qnode
{
	int l,r,id,ans,pos;
} query[MAX_V];

int ans,n,q,t,block,id[MAX_V];
bool num[MAX_V];

inline bool cmp1(qnode x,qnode y)
{
	return x.pos==y.pos?x.r<y.r:x.pos<y.pos;
}

inline bool cmp2(qnode x,qnode y)
{
	return x.id<y.id;
}

inline void add(int x)
{
	if (!x) return;
	num[x]=1;
	if (num[x-1]&&num[x+1]) ans--;
	if (!num[x-1]&&!num[x+1]) ans++;
}

inline void del(int x)
{
	if (!x) return;
	num[x]=0;
	if (num[x-1]&&num[x+1]) ans++;
	if (!num[x-1]&&!num[x+1]) ans--;
}

int main()
{
	scanf("%d",&t);
	while (t--)
	{
		scanf("%d%d",&n,&q);
		for(int i=1;i<=n;i++)
			scanf("%d",&id[i]);
		block=sqrt(n); ans=0;
		for(int i=1;i<=q;i++)
		{
			scanf("%d%d",&query[i].l,&query[i].r);
			query[i].id=i; query[i].pos=query[i].l/block;
		}
		int l=1,r=0;
		memset(num,0,sizeof(num));
		sort(query+1,query+q,cmp1);
		for(int i=1;i<=q;i++)
		{
			if (query[i].r<l||query[i].l>r)
			{
				while (l<=r) num[id[l++]]=0;
				ans=0;
				l=query[i].l,r=query[i].l-1;
				while (r<query[i].r) add(id[++r]);
				query[i].ans=ans; continue;
			}
			while (l>query[i].l) add(id[--l]);
			while (l<query[i].l) del(id[l++]);
			while (r>query[i].r) del(id[r--]);
			while (r<query[i].r) add(id[++r]);
			query[i].ans=ans;
		}
		sort(query+1,query+q,cmp2);
		for(int i=1;i<=q;i++)
			printf("%d\n",query[i].ans);
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值