USACO2014Open Fair Photography

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 100005
using 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 <script type="math/tex" id="MathJax-Element-23">m</script>步得到的集合,再枚举右端点用map维护就可以了。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#define M 100005
#define P 99937
#define ll unsigned long long
using 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
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值