Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 208 Accepted Submission(s): 122
(转)思路:显然,我们要使得value最大,就要尽量将连续的ID分在一组,所以问题转化为求一个区间中连续ID区间的个数。我们从左往右扫描,依次考虑右端点为i的询问,设dp[l]为区间[l,i]的连续区间个数,po[i]为i出现的位置,若还未出现,则为0,设我们当前考虑的右端点为a[i],首先我们假设a[i]不能和区间[1,i-1]中的任何一个数分到一组,则我们要将dp[1]到dp[i-1]全部加1,然后考虑po[a[i]+1]是否不为0,若不为0则说明a[i]-1已经在前面出现,则我们需要将dp[1]到dp[po[a[i]+1]]全部减一个1,因为a[i]可以和a[i]+1分为一组,则我们之前加的1是多余的。对于a[i]-1的情况同理。以上操作可以由线段树或者树状数组什么的实现,然后再将询问按照右端点从小到大排序,离线处理即可,以下是代码实现
线段树:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=100010; #define L(rt) (rt<<1) #define R(rt) (rt<<1|1) struct Tree{ int l,r; int num; //num记录(l,r)区间内的组数 }tree[N<<2]; struct node{ int l,r; int id; }f[N]; int cmp(node a,node b){ return a.r<b.r; } int a[N],loc[N],res[N]; void build(int L,int R,int rt){ tree[rt].l=L; tree[rt].r=R; tree[rt].num=0; if(tree[rt].l==tree[rt].r) return ; int mid=(L+R)>>1; build(L,mid,L(rt)); build(mid+1,R,R(rt)); } void update(int L,int R,int val,int rt){ if(tree[rt].l==L && tree[rt].r==R){ tree[rt].num+=val; return ; } int mid=(tree[rt].l+tree[rt].r)>>1; if(R<=mid) update(L,R,val,L(rt)); else if(L>=mid+1) update(L,R,val,R(rt)); else{ update(L,mid,val,L(rt)); update(mid+1,R,val,R(rt)); } tree[rt].num=tree[L(rt)].num+tree[R(rt)].num; } int query(int L,int R,int rt){ if(tree[rt].l==L && tree[rt].r==R) return tree[rt].num; int mid=(tree[rt].l+tree[rt].r)>>1; if(R<=mid) return query(L,R,L(rt)); else if(L>=mid+1) return query(L,R,R(rt)); else{ int a=query(L,mid,L(rt)); int b=query(mid+1,R,R(rt)); return a+b; } } int main(){ //freopen("input.txt","r",stdin); int t,n,m; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); loc[a[i]]=i; } for(int i=1;i<=m;i++){ scanf("%d%d",&f[i].l,&f[i].r); f[i].id=i; } sort(f+1,f+m+1,cmp); //将查找区间以右右区间递增排序,方便在更改线段树的时候查找,不会漏掉 build(1,n,1); int i,j=1; for(i=1;i<=n;i++){ update(i,i,1,1); //每次新进的一位数,假设独立,所有已i结尾的区间都+1 if(a[i]<n && loc[a[i]+1]<i) //每次删除掉之前加入的与a[i]相邻的数,因为它们在一组里 update(loc[a[i]+1],loc[a[i]+1],-1,1); if(a[i]>1 && loc[a[i]-1]<i) update(loc[a[i]-1],loc[a[i]-1],-1,1); while(j<=m && f[j].r==i){ res[f[j].id]=query(f[j].l,f[j].r,1); j++; } } for(i=1;i<=m;i++) printf("%d\n",res[i]); } return 0; }
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=100010; int n,m; int arr[N],a[N],loc[N],res[N]; struct node{ int l,r; int id; }f[N]; int cmp(node a,node b){ return a.r<b.r; } int lowbit(int x){ return x&(-x); } void update(int i,int val){ while(i<=n){ arr[i]+=val; i+=lowbit(i); } } int sum(int i){ int ans=0; while(i>0){ ans+=arr[i]; i-=lowbit(i); } return ans; } int main(){ //freopen("input.txt","r",stdin); int t; //刚开在这里使用 int t,n,m; 这里的局部变量影响了在main函数外面定义的全局变量n(默认值为0),导致我无奈了。。。。。 scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); loc[a[i]]=i; } for(int i=1;i<=m;i++){ scanf("%d%d",&f[i].l,&f[i].r); f[i].id=i; } sort(f+1,f+m+1,cmp); //将查找区间以右右区间递增排序,方便在更改线段树的时候查找,不会漏掉 memset(arr,0,sizeof(arr)); int i,j=1; for(i=1;i<=n;i++){ update(i,1); //每次新进的一位数,假设独立,所有已i结尾的区间都+1 if(a[i]<n && loc[a[i]+1]<i) //每次删除掉之前加入的与a[i]相邻的数,因为它们在一组里 update(loc[a[i]+1],-1); if(a[i]>1 && loc[a[i]-1]<i) update(loc[a[i]-1],-1); while(j<=m && f[j].r==i){ res[f[j].id]=sum(f[j].r)-sum(f[j].l-1); j++; } } for(i=1;i<=m;i++) printf("%d\n",res[i]); } return 0; }