百度之星2010 复赛2 购物搜索调研

来源:互联网 发布:php redis watch 编辑:程序博客网 时间:2024/06/10 01:31

原题:

问题描述 

       “有啊”是百度旗下的电子商务购物平台,每天都有上百万的用户在平台上搜索自己想要购买的商品。为了给用户更 好的搜索体验,工程师们准备做一次搜索策略的调研工作。

       假设对于某一个特定的查询词来说,一共有N个相关商品,并且通过人工标注得到每个商品与查询词的相关度reli。 当用户输入这个查询词时,可以采取一种非常简单的响应策略,就是在N个相关商品中随机的找一段连续的商品子序列返回给用户。 工程师认为,在每次返回结果中相关度的最小值代表了本次搜索结果的分数score。

       用户在搜索时,心中往往会对结果有个期望分数expect,当搜索结果的分数score高于用户的预期 expect时,该用户的满意度为score – expect;否则用户的满意度为0。

       假设在上述响应策略中,返回的子序列是完全随机的(即每段连续子序列被选中的概率完全相同),你的任务是计算 每个用户满意度的数学期望。

输入格式 

       输入第一行包含一个正整数T,表示测试数据的组数。每组测试数据的第一行为相关商品的数量N, 第二行为N个整数,分别表示每个商品与查询词的相关度reli(用空格分隔)。 第三行为一个正整数M,表示参与调研的用户数量,接下来的M行中的每一行都是一个整数expect,表示该用户在搜索时预期的分数值。 其中1 <= T <= 10,1 <= N, M <= 100,000, -109 <= reli, expect <= 109。

输出格式 

       对于每组测试数据,首先输出一行 “Case #:”,其中#表示测试数据编号(从1开始),注意空格与大小写。 接下来的M行,按照输入顺序依次给出每个用户在这种响应策略下满意度的期望值,每个用户占一行。 为了避免精度问题,期望值用最简分数 “A/B” 表示,其中A与B互质,B是正整数。当结果是整数时,应只输出A。

样例输入 

       3

       3

       13 5

       1

       2

       4

       5-2 1 4

       2

       1

       -1

       3

       24 10

       1

       0

       样例输出

        Case1:

        5/6

       Case2:

        7/10

        3/2

       Case3:

        4

样例说明 

在Case 1中,可能返回的子序列一共有6个[1], [3], [5], [1 3], [3 5], [1 3 5],对应的分数(相关度最小值)为1, 3, 5, 1, 3, 1,用户满意度依次为0, 1, 3, 0, 1, 0。每个子序列被返回的概率相同,即为1/6。根据数学期望的定义 可以得出该用户的满意度期望值是5/6。


题意分析:

题意比较明确,给一个长度为n的序列ai,然后M次询问。每次询问是给出一个数k,然后在序列A里面随机取一段连续的序列,其中这个序列中的最小值为x,然后这个序列的得分是max(x-k, 0),问得分的数学期望是多少。


关键思路:

总共可能产生的序列是n*(n+1)/2,这个是要求的数学期望的分母,关键是分子怎么求。

我们计算将每一个ai作为最小值的序列个数。首先可以通过单调栈处理出ai左边第一个比ai小的位置li,右边第一个比ai小的位置ri,那么在任何序列左端点在[li+1,i]之间,右端点在[i,ri-1]之间的最小值都是ai,个数是(i-li)*(ri-i)。

     但有一种特殊情况,就是[li+1,ri-1]这端区间内不止一个数为ai,也就是最小值不唯一。比如3 2 4 2,在处理第一个2的时候会得到区间{2}.{3,2},{2,4},{3,2,4},{2,4,2},{3,2,4,2},在处理第二个2的时候{3,2,4,2},{2,4,2}这两个区间重复计算了。也就是说同一段区间里面有几个不同的最小值需要去重。

     解决方法也很简单:假设当前处理区间为[l,r],最小值为ai,位置分别为l < p1 <= p2 <= ... <= pm < r,处理p1的时候依然是(p1 - l)*(r - p1),处理p2的时候发现多出的部分为左端点在[p1+1,p2],右端点在[p2,r-1]的部分,因此去重后应当加(p2 - p1) * (r - p2),后面都是同理了。这里需要处理一下每个数前一个和他相同的数的位置。

    最后算答案的时候就是sigma((ai - k)*gi)/(n*(n+1)/2) (ai >= k, gi 为最小值为ai的序列个数),由于询问次数很多,考虑每次取的都是较大的几个ai,排序后处理一下ai的后缀和以及ai*gi后缀和,然后根据查询的k二分位置给出答案就完成了。

测试数据:

4

3

1 3 5

1

2

4

5 -2 1 4

2

1

-1

3

2 4 10

1

0

3

2 2 2

1

1



代码:

#include<iostream>#include<cstdio>#include<cstdlib>#include<algorithm>#include<cstring>#include<string>#include<vector>#include<cmath>#include<set>#include<stack>#include<map>using namespace std;typedef long long LL;#define CLR(a,b) memset(a,b,sizeof(a))const int N = 100000+20;LL gcd(LL a,LL b){    if(a == 0)return b;    return gcd(b%a,a);}map<int,LL> G;typedef pair<int,int> PII;int n,m;int a[N];LL l[N],r[N];int bef[N];PII o[N];LL sufix[N],sufixsum[N];int tot;map<int,LL>::iterator ite;void solve(LL x){    int pos = upper_bound(o + 1, o + tot + 1, (PII)make_pair(x, 0)) - o;    LL p = sufixsum[pos] - sufix[pos] * x;    LL q = n * (n+1) / 2;    LL d = gcd(p,q);    p /= d; q /= d;    if(q == 1) printf("%lld\n",p);    else printf("%lld/%lld\n",p,q);}void init(){    CLR(bef,-1);    sort(o + 1, o + n + 1);    tot = 0;    for(int i = 1 ; i <= n ; i ++){        if(i > 1  && o[i].first == o[i-1].first){            bef[o[i].second] = o[i-1].second;        }else o[++tot] = o[i];    }    stack<int> st;    for(int i = 1; i <= n ; i++){        while(!st.empty() && a[st.top()] >= a[i])st.pop();        if(st.empty())l[i] = 0;        else l[i] = st.top();        st.push(i);    }    while(!st.empty())st.pop();    for(int i = n; i >= 1 ; i--){        while(!st.empty() && a[st.top()] >= a[i])st.pop();        if(st.empty())r[i] = n+1;        else r[i] = st.top();        st.push(i);    }    while(!st.empty())st.pop();    G.clear();    for(int i = 1; i <= n ; i ++){        if(bef[i] == -1){            G[a[i]] += (i - l[i]) * (r[i] - i);        }else{            if(r[bef[i]] < i){                G[a[i]] += (i - l[i]) * (r[i] - i);            }else{                G[a[i]] += (i - bef[i]) * (r[i] - i);            }        }    }    sufixsum[tot+1] = sufix[tot+1] = 0;    for(int i = tot ; i >= 1 ; i--){        int val = o[i].first;        sufix[i] = sufix[i+1] + G[val];        sufixsum[i] = sufixsum[i+1] + val * G[val];    }}int main(){    int T,cas = 0;    scanf("%d",&T);    while(T--){        cas ++;        scanf("%d",&n);        for(int i = 1; i <= n ;i ++){            scanf("%d",&a[i]);            o[i] = make_pair(a[i], i);        }        init();        int m;        scanf("%d",&m);        printf("Case %d:\n",cas);        for(int i = 0; i < m ; i ++){            int x;            scanf("%d",&x);            solve(x);        }    }    return 0;}


0 0