标签:hdu5172 gtys gay friends 线段树单点更新 bestcoder#29
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5172
题意:给出n个数,m个询问,问你[l,r]区间内是否为1到r-l+1的全排列。 大小很容易我们通过记录前缀和很容易求出来,但是关键是去重。 考虑线段树做法,我们记录每个点的靠左最近的相同元素的位置,然后求 整个区间的最大值(即最大的前驱)如果小于l,即满足条件,输出YES。
好吧,其实这个题目我是搜的RMQ算法出来的,因为我想练一下RMQ算法,所以我就看了一下别人的博客,自己也写了一下,结果死活MLE。。。
好吧,我仔细看了一下,1000000的数据量用RMQ开一个二维数组1000000*20,显然会超内存。。。
结果我不得不老老实实的滚回去用线段树写了。。。orz....
RMQ的MLE代码:
#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<queue>
#include<map>
#include<cmath>
#include<stack>
#include<set>
#include<vector>
#include<algorithm>
#define LL long long
#define inf 1<<30
#define sf(a) scanf("%d",&a);
#define CLEAR(a,b) memset(a,b,sizeof(a))
using namespace std;
/*
TLE...显然超内存;
*/
const int N=1000005;
int n,m,a,b;
int num[N],pre[N];
int dp[N][21];
int sum[N];
void ST(int len)
{
for(int i=1;i<=n;i++) dp[i][0]=num[i];
for(int j=1;1<<j < n;j++){
for(int i=1;i+(1<<j)-1<n;i++){
dp[i][j]=max(dp[i][j-1],dp[i+1<<(j-1)][j-1]);
}
}
}
int rmq(int s,int v)
{
int k=(int)(log(v-s+1)*1.0/log(2.0));
return max(dp[s][k],dp[v-1<<k+1][k]);
}
int main()
{
while(~scanf("%d%d",&n,&m)){
CLEAR(pre,0);
CLEAR(sum,0);
for(int i=1;i<=n;i++){
sf(a);
sum[i]+=sum[i-1]+a;
num[i]=pre[a];
pre[a]=i;
}
ST(n);
while(m--){
sf(a);sf(b);
double tmp=(b-a+2)*(b-a+1)*1.0/2.0;
if(tmp!=sum[b]-sum[a-1]){
//cout<<tmp<<' '<<sum[b]-sum[a-1]<<' ';
printf("NO\n");
continue;
}else{
if(rmq(a,b)<a) printf("YES\n");
else printf("NO\n");
}
}
}
return 0;
}
线段树AC代码:
#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<queue>
#include<map>
#include<cmath>
#include<stack>
#include<set>
#include<vector>
#include<algorithm>
#define LL long long
#define inf 1<<30
#define s(a) scanf("%d",&a)
#define CLEAR(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=1000005;
int n,m,a,b;
int pre[N],sum[N]; // sum存总和,pre定位该数字上一次出现的位置;
struct node
{
int l,r;
int pre; // 用线段树维护最近一个重复的数字;
}node[N<<2];
void PushUp(int rt)
{
node[rt].pre=max(node[rt<<1].pre,node[rt<<1|1].pre);
}
void build(int l,int r,int rt)
{
node[rt].l=l;
node[rt].r=r;
node[rt].pre=0;
if(l!=r){
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
}
}
void Insert(int v,int p,int rt)
{
int ll=node[rt].l,rr=node[rt].r;
if(ll==rr&&p==ll){
node[rt].pre=pre[v];
return;
}
int mid=(ll+rr)>>1;
if(p<=mid) Insert(v,p,rt<<1);
else Insert(v,p,rt<<1|1);
PushUp(rt);
}
int query(int l,int r,int rt)
{
int ll=node[rt].l,rr=node[rt].r;
if(ll==l&&rr==r){
return node[rt].pre;
}
int mid=(ll+rr)>>1;
if(r<=mid) return query(l,r,rt<<1);
else if(l>mid) return query(l,r,rt<<1|1);
else return max(query(l,mid,rt<<1),query(mid+1,r,rt<<1|1));
}
int main()
{
while(~s(n)){
s(m);
CLEAR(pre,0);
CLEAR(sum,0);
build(1,n,1);
for(int i=1;i<=n;i++){
s(a);
sum[i]+=sum[i-1]+a;
Insert(a,i,1);
pre[a]=i;
}
while(m--){
s(a);s(b);
int tmp=(b-a+2)*(b-a+1)*1.0/2.0; // 如果是全排列那么最后的和必然符合这个公式;
if(sum[b]-sum[a-1]!=tmp){
printf("NO\n");
continue;
}else{
if(query(a,b,1)<a) printf("YES\n"); // 查询该区间最近一个重复的数字出现的位置,如果比a小,那就说明这个区间没有出现过重复的数字;
else printf("NO\n");
}
}
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
HDU-5172-GTY's gay friends-线段树单点更新
标签:hdu5172 gtys gay friends 线段树单点更新 bestcoder#29
原文地址:http://blog.csdn.net/wlxsq/article/details/47281165