USACO2014Open Fair Photography

来源:互联网 发布:ubuntu如何安装wine 编辑:程序博客网 时间:2024/05/19 07:41

Fair Photography

FJ’s N cows (1 <= N <= 100,000) are standing at various positions along a long one-dimensional fence. The ith cow is standing at position x_i (an integer in the range 0…1,000,000,000) and has breed b_i (an integer in the range 1..8). No two cows occupy the same position.
FJ wants to take a photo of a contiguous interval of cows for the county fair, but we wants all of his breeds to be fairly represented in the photo. Therefore, he wants to ensure that, for whatever breeds are present in the photo, there is an equal number of each breed (for example, a photo with
27 each of breeds 1 and 3 is ok, a photo with 27 of breeds 1, 3, and 4 is ok, but 9 of breed 1 and 10 of breed 3 is not ok). Farmer John also wants at least K (K >= 2) breeds (out of the 8 total) to be represented in the photo. Help FJ take his fair photo by finding the maximum size of a photo that satisfies FJ’s constraints. The size of a photo is the difference between the maximum and minimum positions of the cows in the photo.
If there are no photos satisfying FJ’s constraints, output -1 instead.

PROBLEM NAME: fairphoto

INPUT FORMAT:

Line 1: N and K separated by a space
Lines 2..N+1: Each line contains a description of a cow as two integers separated by a space; x(i) and its breed id.

SAMPLE INPUT (file fairphoto.in):

9 2
1 1
5 1
6 1
9 1
100 1
2 2
7 2
3 3
8 3

INPUT DETAILS:

Breed ids: 1 2 3 - 1 1 2 3 1 - … - 1
Locations: 1 2 3 4 5 6 7 8 9 10 … 99 100

OUTPUT FORMAT:

Line 1: A single integer indicating the maximum size of a fair photo. If no such photo exists, output -1.

SAMPLE OUTPUT (file fairphoto.out):

6

OUTPUT DETAILS:

The range from x = 2 to x = 8 has 2 each of breeds 1, 2, and 3. The range from x = 9 to x = 100 has 2 of breed 1, but this is invalid because K = 2 and so we must have at least 2 distinct breeds.

Task:
给定一行n头奶牛的位置与品种。现在要选出一个区间的奶牛,使得其中的出现的奶牛种类大于等于k种且每种奶牛的头数相等。求这样区间的最长长度。(n<=10,0000)。种类数k<=8
Solution:
首先考虑O(n2)做法,这可以用前缀和简单地完成,直接枚举左右端点即可。

#include<stdio.h>#include<iostream>#include<algorithm>#define M 100005using namespace std;struct Node{    int x,c;    bool operator <(const Node &a)const{        return x<a.x;    }}A[M];int sum[10][M];int main(){    int n,k,ans=-1;    scanf("%d %d",&n,&k);    for(int i=1;i<=n;i++)        scanf("%d %d",&A[i].x,&A[i].c);    sort(A+1,A+n+1);    for(int i=1;i<=n;i++)        for(int j=1;j<=8;j++)            if(j==A[i].c)sum[j][i]=sum[j][i-1]+1;            else sum[j][i]=sum[j][i-1];    for(int i=1;i<=n;i++)            for(int j=i+1;j<=n;j++){            if(A[j].x-A[i].x<=ans)continue;            int cnt=0,sm=-1;            for(int t=1;t<=8;t++){                if(sum[t][j]-sum[t][i-1]==0)continue;                if(sm==-1)sm=sum[t][j]-sum[t][i-1];                if(sm!=sum[t][j]-sum[t][i-1]){                    cnt=-1;                    break;                }else cnt++;            }            if(cnt>=k){                if(A[j].x-A[i].x>ans)ans=A[j].x-A[i].x;            }        }    printf("%d\n",ans);    return 0;}

然后,我们就要考虑如何枚举右端点,直接算出左端点的位置。我们发现,对于左右两个满足条件的端点处的前缀,各种种类的牛的差值是一样的。计O(n2)做法中的前缀为T(b,p)表示[1,p]区间中b种类牛出现的个数。则我们现在需要的前缀记为S(A,p)

  • A是一些种类组成的集合
  • S(A,p)包括每一个在A中的种类与第一个在A中的种类在区间[1,p]中的个数之差

我们现在只需要对于一个S(A,R),找到一个相等的S(A,L)即可。首先因为种类数较少,我们可以先枚举这个A集合。然后,我们可以在向右枚举的过程中直接算出这个S(A,R)并放到一个容器中去维护。可以采用map,但是速度太慢,因此采用哈希表就可以O(2kn)卡过这道题了。
标程的做法好像是O(k2n)的,它的优化思想就是当左端点向左移时,集合A中的元素最多只会改变k次,然后就可以预处理出一个点向前向后m步得到的集合,再枚举右端点用map维护就可以了。

#include<stdio.h>#include<iostream>#include<algorithm>#define M 100005#define P 99937#define ll unsigned long longusing namespace std;struct Node1{    int x,c;    bool operator <(const Node1 &a)const{        return x<a.x;    }}A[M];int n,k,ans=-1,mx=0;bool mark[8];int Q[M],top=0;struct HASH{    int cnt;    int nxt[M],last[M],val[M];    ll Hash[M];    void insert(ll H,int t){        int x=H%P;        Q[++top]=x;        cnt++;        nxt[cnt]=last[x];val[cnt]=t;Hash[cnt]=H;        last[x]=cnt;    }    int find(ll H){        int x=H%P;        for(int i=last[x];i;i=nxt[i])            if(Hash[i]==H)return val[i];        return -1;    }    void clear(){        while(top)last[Q[top--]]=0;        cnt=0;    }}mp;void Rd(int &res){    res=0;    char c;    while(c=getchar(),!isdigit(c));    do{        res=(res<<1)+(res<<3)+c^48;    }while(c=getchar(),isdigit(c));}ll GetHash(int cnt[]){    ll res=0;    for(int i=0;i<mx;i++)        res=res*1000000009+cnt[i]*233;    return res;}void check(){    int cnt[8]={0};    int p=0;    while(!mark[p])p++;    mp.clear();    mp.insert(0,1);    for(int R=1;R<=n;R++){        if(!mark[A[R].c]){            mp.clear();            for(int i=0;i<mx;i++)cnt[i]=0;            mp.insert(0,R+1);        }else{            if(A[R].c==p){                for(int j=p+1;j<mx;j++)                    if(mark[j])cnt[j]--;            }else cnt[A[R].c]++;            ll h=GetHash(cnt);            int t=mp.find(h);            if(t==-1)mp.insert(h,R+1);            else{                if(A[R].x-A[t].x>ans)ans=A[R].x-A[t].x;            }        }    }}void dfs(int x,int cnt){    if(x==mx){        if(cnt>=k)check();        return;    }    mark[x]=true;    dfs(x+1,cnt+1);    mark[x]=false;    dfs(x+1,cnt);}int main(){    scanf("%d %d",&n,&k);    for(int i=1;i<=n;i++){        scanf("%d %d",&A[i].x,&A[i].c);        if(A[i].c>mx)mx=A[i].c;        A[i].c--;    }    sort(A+1,A+n+1);    dfs(0,0);    printf("%d\n",ans);    return 0;}
1 0
原创粉丝点击