HDU 5172 GTY's gay friends(HASH 随机算法)

【题意】给出一个长度为n的数组(n<=100W).每个数都<=100W。然后m次询问,每次询问l..r区间内是否为1..r-l+1的一个排列

【解题方法】一个区间是排列只需要区间和为\frac{len(len+1)}{2}2len(len+1)(lenlen为区间长度),且互不相同,对于第一个问题我们用前缀和解决,对于第二个问题,预处理每个数的上次出现位置,记它为pre,互不相同即区间中pre的最大值小于左端点,使用线段树或Sparse Table即可在O(n)/O(nlogn)O(n)/O(nlogn)的预处理后 O(logn)/O(1)O(logn)/O(1)回答每个询问.不过我们还有更简单的hash做法,对于[1..n][1..n]中的每一个数随机一个64位无符号整型作为它的hash值,一个集合的hash值为元素的异或和,预处理[1..n][1..n]的排列的hash和原序列的前缀hash异或和,就可以做到线性预处理,O(1)O(1)回答询问.

【AC 代码】

<span style="font-family:Arial;">//Hash 算法</span>
#include <time.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1000010;
#define uLL unsigned __int64
uLL XOR[maxn],a[maxn],sum[maxn];
inline uLL RANDuLL()
{
    uLL one=1;
    uLL RAND=(rand()+rand())*(one<<47)+(rand()+rand())*(one<<31)+(rand()+rand())*(one<<15)+(rand()+rand());
    return RAND;
}

int main()
{
    srand(time(NULL));
    XOR[0]=0;
    for(int i=1; i<maxn; i++){
        a[i]=RANDuLL();
        XOR[i]=XOR[i-1]^a[i];
    }
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        sum[0]=0;
        int x;
        for(int i=1; i<=n; i++){
            scanf("%d",&x);
            sum[i]=a[x]^sum[i-1];
        }
        int l,r;
        while(m--){
            scanf("%d%d",&l,&r);
            if(XOR[r-l+1]==(sum[r]^sum[l-1])){
                puts("YES");
            }else{
                puts("NO");
            }
        }
    }
}

【线段树版本】
<span style="font-family:Arial;">//代码来自上决</span>
<pre name="code" class="cpp">#include<iostream>  
#include<algorithm>  
#include<stdio.h>  
#include<string.h>  
using namespace std;  
typedef __int64 LL;  
#define NN 1000010  
struct node  
{  
    int l,r;  
    int id;  
}q[NN];  
bool cmp(node t1,node t2)  
{  
    return t1.r<t2.r;  
}  
int mx[NN*3];  
LL sum[NN*3];  
void push_up(int id)  
{  
    sum[id]=sum[id<<1]+sum[id<<1|1];  
    mx[id]=max(mx[id<<1],mx[id<<1|1]);  
}  
int MAX;  
LL SUM;  
void build()  
{  
    memset(mx,0,sizeof(mx));  
    memset(sum,0,sizeof(sum));  
}  
void update(int id,int L,int R,int pos,int val,int mxx)  
{  
    if(L==R)  
    {  
        sum[id]=(LL)val;  
        mx[id]=mxx;  
        return ;  
    }  
    int mid=(L+R)>>1;;  
    if(pos<=mid)  
        update(id<<1,L,mid,pos,val,mxx);  
    else  
        update(id<<1|1,mid+1,R,pos,val,mxx);  
    push_up(id);  
}  
void query(int id,int L,int R,int l,int r)  
{  
    if(l<=L&&R<=r)  
    {  
        MAX=max(MAX,mx[id]);  
        SUM+=sum[id];  
        return ;  
    }  
    int mid=(L+R)>>1;  
    if(l<=mid)  
        query(id<<1,L,mid,l,r);  
    if(mid<r)  
        query(id<<1|1,mid+1,R,l,r);  
}  
int a[NN],p[NN],pre[NN];  
inline int getint()  
{  
    int c;  
    while (c = getchar(), c<'0' || '9'<c);  
    int res = c - 48;  
    while (c = getchar(), '0' <= c&&c <= '9') res = (res << 3) + res + res + c - 48;  
    return res;  
}  
int main()  
{  
    int n,m;  
    while(scanf("%d %d",&n,&m)!=EOF)  
    {  
        for(int i=1;i<=n;i++) a[i]=getint();  
        for(int i=1;i<=m;i++)  
        {  
            q[i].l=getint();  
            q[i].r=getint();  
            q[i].id=i;  
        }  
        sort(q+1,q+m+1,cmp);  
        build();  
        memset(p,0,sizeof(p));  
        memset(pre,0,sizeof(pre));  
        for(int i=1;i<=n;i++)  
        {  
            pre[i]=p[a[i]];  
            p[a[i]]=i;  
        }  
        int t=1;  
        memset(p,0,sizeof(p));  
        for(int i=1;i<=n;i++)  
        {  
            update(1,1,n,i,a[i],pre[i]);  
            while(i==q[t].r)  
            {  
                SUM=0;  
                MAX=0;  
                LL tt=(LL)(q[t].r-q[t].l+1);  
                //printf("%d %d  %d??\n",q[t].l,q[t].r,q[t].id);  
                LL sum=tt*(tt+1)/2;  
                query(1,1,n,q[t].l,q[t].r);  
                //printf("%I64d %d   %I64d  %d\n",tmp.sum,tmp.mx ,sum,q[t].l);  
                if(sum==SUM && MAX < q[t].l)  
                {  
                    p[q[t].id]=1;  
                }  
                ++t;  
            }  
        }  
        for(int i=1;i<=m;i++)  
        {  
            if(p[i]==1)  
                puts("YES");  
            else puts("NO");  
        }  
  
    }  
    return 0;  
}  

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值