题目大意:
n个人的编号是从1 - n ,现在他们无序的站成一排。
第 id 号人和 id-1 id +1 号人是朋友,
朋友之间可以组成group。
一个group的值等于他们人数的平方。
然后有m次询问,问给出的l r 之间能构成group值的和的最大值的group数。
思路分析 (莫队算法):
首先,我们面临着假设你知道这个区间有多少个group 你能知道得到最大值的时候group是怎么分配的么。
令 x = a + b
x^2 = (a+b)^2 = a^2 + 2ab + b^2
可以得到如果得到了group 那么就不要分裂,因为group在一起的才能组成最大值。
那么现在解决询问的问题。
我们用分块莫队算法直接跑。
用ex 记录哪个id的人存在了。
加入的时候通过判断左右两人是否存在。
然后仔细推一下就知道加入与删除的关系了。
(离线线段树)
我们假设所有新加入到树中的数就是孤立的。然后我们再去判断他左边右边是否在树中,如果在树中那么就取消他们所在区间的标记。
借着
5 1
2 1 4 3 5
1 5
更好理解这份代码。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define maxn 100005
using namespace std;
int bel[maxn];
int block;
inline void scanf_(int &num){
char in;
bool neg=false;
while(((in=getchar()) > '9' || in<'0') && in!='-') ;
if(in=='-'){
neg=true;
while((in=getchar()) >'9' || in<'0');
}
num=in-'0';
while(in=getchar(),in>='0'&&in<='9')
num*=10,num+=in-'0';
if(neg)
num=0-num;
}
inline void printf_(int num){
bool flag=false;
if(num<0){
putchar('-');
num=-num;
}
int ans[10],top=0;
while(num!=0){
ans[top++]=num%10;
num/=10;
}
if(top==0)
putchar('0');
for(int i=top-1;i>=0;i--){
char ch=ans[i]+'0';
putchar(ch);
}
}
struct node
{
int st,ed,ans,id;
bool operator < (const node &cmp)const
{
return bel[st]<bel[cmp.st] || (bel[st]==bel[cmp.st] && ed<cmp.ed);
}
}Q[maxn];
bool cmp_id(node a,node b)
{
return a.id<b.id;
}
int a[maxn];
int ex[maxn];
void modify(int num,int &tans,int add)
{
if(add==1)
{
if(ex[num-1] && ex[num+1])tans--;
else if(!ex[num-1] && !ex[num+1])tans++;
}
else
{
if(ex[num-1] && ex[num+1])tans++;
else if(!ex[num-1] && !ex[num+1])tans--;
}
ex[num]+=add;
}
int main()
{
int T;
scanf_(T);
while(T--)
{
memset(ex,0,sizeof ex);
int n,m;
scanf_(n),scanf_(m);
block=(int)sqrt(1.0*n);
for(int i=1;i<=n;i++)
{
scanf_(a[i]);
bel[i]=(i-1)/block+1;
}
for(int i=1;i<=m;i++)
{
scanf_(Q[i].st);
scanf_(Q[i].ed);
Q[i].id=i;
}
sort(Q+1,Q+1+m);
int tans=0;
for(int l=1,r=0,i=1;i<=m;i++)
{
if(r<Q[i].ed)
{
for(r=r+1;r<=Q[i].ed;r++)
modify(a[r],tans,1);
r--;
}
if(r>Q[i].ed)
{
for(;r>Q[i].ed;r--)
modify(a[r],tans,-1);
}
if(l<Q[i].st)
{
for(;l<Q[i].st;l++)
modify(a[l],tans,-1);
}
if(l>Q[i].st)
{
for(l=l-1;l>=Q[i].st;l--)
modify(a[l],tans,1);
l++;
}
Q[i].ans=tans;
}
sort(Q+1,Q+1+m,cmp_id);
for(int i=1;i<=m;i++)
{
printf_(Q[i].ans);
puts("");
}
}
return 0;
}
离线线段树。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lson num<<1,s,mid
#define rson num<<1|1,mid+1,e
#define mid ((s+e)>>1)
#define maxn 100005
using namespace std;
int cnt[maxn<<2];
void update(int num,int s,int e,int pos,int val)
{
if(s==e){
cnt[num]+=val;
return;
}
if(pos<=mid)update(lson,pos,val);
else update(rson,pos,val);
cnt[num]=cnt[num<<1]+cnt[num<<1|1];
}
int query(int num,int s,int e,int l,int r)
{
if(l<=s && r>=e)return cnt[num];
if(r<=mid)return query(lson,l,r);
else if(l>mid)return query(rson,l,r);
else return query(lson,l,mid)+query(rson,mid+1,r);
}
struct node
{
int st,ed,id,ans;
bool operator < (const node &cmp)const
{
return ed<cmp.ed;
}
}Q[maxn];
bool cmp_id(node a,node b)
{
return a.id<b.id;
}
int pos[maxn];
int a[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pos[a[i]]=i;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&Q[i].st,&Q[i].ed);
Q[i].id=i;
}
sort(Q+1,Q+1+m);
int index=1;
memset(cnt,0,sizeof cnt);
for(int i=1;i<=n;i++)
{
update(1,1,n,pos[a[i]],1);
if(pos[a[i]+1]<=i && a[i]<n)update(1,1,n,pos[a[i]+1],-1);
if(pos[a[i]-1]<=i && a[i]>1)update(1,1,n,pos[a[i]-1],-1);
while(index<=m && Q[index].ed==i)
{
Q[index].ans=query(1,1,n,Q[index].st,Q[index].ed);
index++;
}
}
sort(Q+1,Q+1+m,cmp_id);
for(int i=1;i<=m;i++)
printf("%d\n",Q[i].ans);
}
return 0;
}