• bzoj 2038 [2009国家集训队]小Z的袜子(hose)


    这道题的概率求解最难算的就是求出能取到多少对多少颜色相同的袜子

    因为询问次数过多,这里不能一个个求解询问,需要将询问分块后访问

    先这么理解:

    令cnt[i] 表示 颜色 i 在当前的 l ~ r 的区域内出现的次数 , 此时颜色相同的袜子对数为 tmp

    比如说此时 r 向前一步到r+1 , 那么此时除了val[r+1]这个颜色的袜子数量+1了,其他颜色的袜子颜色是不会变化的,所以总的相同颜色的袜子的对数只要计算val[r+1]这个颜色就可以了

    那么在向前一步之前,val[r+1]这个颜色的可组成一对袜子的总数为 cnt[val[r+1]]*(cnt[val[r+1]]-1) , 之后是cnt[val[r+1]]*(cnt[val[r+1]]+1)

    所以tmp 就是 tmp = tmp - cnt[val[r+1]]*(cnt[val[r+1]]-1) + cnt[val[r+1]]*(cnt[val[r+1]]+1)

    r往后走,或者l往前往后走一样的方式思考

    那么我们主要考虑的就是 l ,r 指针移动的次数,我们总希望能将这样的询问排成一个合理序列,那么访问的时候 l , r 指针可以尽可能的少移动

    [l , r] -> [l',r'] 指针移动次数就是 abs(l-l') + abs(r-r');

    那么其实这里我们就相当于类似希望求出一个以曼哈顿距离为边的最小生成树,这就是莫队算法的思想

    但我实在看不懂这个生成树的形成方式,而且代码太长了,就用下方的复杂度略为高一点的方法了

    这里先将袜子的总数量分成 unit = sqrt(n) 个块 , l/unit 越小说明当前l所处的块越前面,所以我们逐个块处理,在每个块中,l的移动不会超过sqrt(n) , 令r在前面块层次相等时,再由小到大排列,这样就尽可能的让 r 指针少做移动

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <iostream>
     4 #include <algorithm>
     5 #include <cmath>
     6 using namespace std;
     7 #define ll long long
     8 #define N 50010
     9 int val[N] , unit , cnt[N];
    10 ll t[2][N];
    11 struct Query{
    12     int l , r , id ;
    13     Query(int l=0 , int r=0 , int id=0):l(l),r(r),id(id){}
    14 }q[N];
    15 
    16 bool cmp(Query a , Query b)
    17 {
    18     if(a.l/unit == b.l/unit) return a.r<b.r;
    19     return a.l/unit < b.l/unit;
    20 }
    21 
    22 ll gcd(ll a , ll b)
    23 {
    24     if(b == 0) return a;
    25     else return gcd(b , a%b);
    26 }
    27 
    28 void solve(int n , int m)
    29 {
    30     memset(cnt , 0 , sizeof(cnt));
    31     int l=1 , r=1 ;
    32     ll tmp=0;
    33     cnt[val[1]]++;
    34     for(int i=1 ; i<=m ; i++){
    35         while(r<q[i].r){
    36             r++;
    37             tmp -= (ll)cnt[val[r]]*(cnt[val[r]]-1);
    38             cnt[val[r]]++;
    39             tmp += (ll)cnt[val[r]]*(cnt[val[r]]-1);
    40         }
    41         while(r>q[i].r){
    42             tmp  -= (ll)cnt[val[r]]*(cnt[val[r]]-1);
    43             cnt[val[r]]--;
    44             tmp += (ll)cnt[val[r]]*(cnt[val[r]]-1);
    45             r--;
    46         }
    47         while(l<q[i].l){
    48             tmp -= (ll)cnt[val[l]]*(cnt[val[l]]-1);
    49             cnt[val[l]]--;
    50             tmp += (ll)cnt[val[l]]*(cnt[val[l]]-1);
    51             l++;
    52         }
    53         while(l>q[i].l){
    54             l--;
    55             tmp  -= (ll)cnt[val[l]]*(cnt[val[l]]-1);
    56             cnt[val[l]]++;
    57             tmp += (ll)cnt[val[l]]*(cnt[val[l]]-1);
    58         }
    59         t[0][q[i].id] = tmp;
    60         t[1][q[i].id] = (ll)(q[i].r-q[i].l)*(q[i].r-q[i].l+1);
    61     }
    62 }
    63 
    64 int main()
    65 {
    66   //  freopen("a.in" , "r" , stdin);
    67     int n , m;
    68     while(~scanf("%d%d" , &n , &m))
    69     {
    70         for(int i=1 ; i<=n ; i++) scanf("%d" , &val[i]);
    71         for(int i=1 ; i<=m ; i++){
    72             scanf("%d%d" , &q[i].l , &q[i].r);
    73             q[i].id=i;
    74         }
    75         unit = (int)sqrt(n+0.5);
    76         sort(q+1 , q+1+m , cmp);
    77 
    78         solve(n , m);
    79 
    80         for(int i=1 ; i<=m ; i++){
    81             if(t[0][i] == 0) t[1][i]=1;
    82             else{
    83                 ll k=gcd(t[0][i] , t[1][i]);
    84                 t[0][i]/=k,t[1][i]/=k;
    85             }
    86             printf("%lld/%lld
    " , t[0][i] , t[1][i]);
    87         }
    88     }
    89     return 0;
    90 }
  • 相关阅读:
    括号匹配(栈)
    扫雷计算概率
    勘探油田(深度优先搜索)
    扫雷
    Data Analysis 总结笔记:Data Cleaning
    Data Analysis 总结笔记:EDA
    C#StringsGetting Started with Strings
    C#中Trim(),TrimStart(),TrimEnd()的实现及用法
    跨站脚本攻击漏洞防范与解决办法
    APACHE日志格式定义,APACHE日志参数说明
  • 原文地址:https://www.cnblogs.com/CSU3901130321/p/4430473.html
Copyright © 2020-2023  润新知