[题解] 2038: [2009国家集训队]小Z的袜子(hose)

莫队,卡常数


题目地址

  • 思路

    • ( ext{Vis[i]})为元素( ext{i})在区间( ext{[L,R]})的出现次数

    • 考虑区间( ext{[L,R]})和元素( ext{i}),首次取出的概率为(frac{Vis[i]}{R-L+1}),再次取出的概率是(frac{Vis[i]-1}{R-L})

    • 对于区间( ext{[L,R]}),答案为(sum_{i=1}^{N}{frac{Vis[i](Vis[i]-1)}{(R-L)*(R-L+1)}})

    • 这样,每次给变( ext{[L,R]}),分子的变化可以通过对前、后两项的值做差得到:

      • 设X=Vis[i],有:

      [(X+1)X-(X)(X-1)=2X ]

    • 莫队这样修改即可。

  • Code

    • /**************************************************************
          Problem: 2038
          User: Bj2002
          Language: C++
          Result: Accepted
          Time:11036 ms
          Memory:2584 kb
      ****************************************************************/
       
      #include <stdio.h>
      #include <string.h>
      #include <algorithm>
      #define GC getchar()
      #define Clean(X,K) memset(X,K,sizeof(X))
      using namespace std ;
      int Qread () {
          int X = 0 ;
          char C = GC ;
          while (C > '9' || C < '0') C = GC ;
          while (C >='0' && C <='9') {
              X = X * 10 + C - '0' ;
              C = GC ;
          }
          return X ;
      }
      long long GCD(long long M, long long N) {
          while (N != 0) {
              long long T = M % N;
              M = N;
              N = T;
          }
          return M;
      }
      const int Maxn = 50005 ;
      int N , M , A[Maxn] , Vis[Maxn]  ;
      long long Ans[Maxn] , Sum[Maxn];
      struct Node {
          int Left , Right  , Place;
      };
      Node Q[Maxn] ;
      bool Cmp (const Node &X , const Node &Y) {
          if (X.Left != Y.Left ) return X.Left < Y.Left ;
          if (X.Left & 1) return X.Right < Y.Right ;
          else return X.Right > Y.Right ;
      }
      bool Cmp2 (const Node &X , const Node &Y) {
          return X.Place < Y.Place ;
      }
      void Qwrite(int X) {
          if(X > 9) Qwrite(X / 10);
          putchar(X % 10 + '0');
      }
       
      int main () {
      //  freopen ("P1494.in" , "r" , stdin) ;
      //  freopen ("P1494.out", "w" , stdout) ;
          N = Qread () , M = Qread ();
          for (int i = 1 ; i <= N; ++ i) A[i] = Qread () ;
          for (int i = 1 ; i <= M; ++ i) Q[i].Left = Qread () , Q[i].Right = Qread () , Q[i].Place = i ;;
          sort (Q + 1 , Q + 1 + M , Cmp) ;
          Clean (Vis , 0) ;
          int L , R ;
          long long Now = 0 ;
          L = R = Q[1].Left ;
          Vis[A[L]] = 1 ;
          for (int i = 1 ; i <= M; ++ i) {
              while (L < Q[i].Left ) {
                  -- Vis[A[L]] ;
                  Now -= (Vis[A[L]] << 1) ;
                  ++ L ;
              }
      //      cout << L <<' '<<R <<' '<<Now<<endl;
              while (R < Q[i].Right ) {
                  ++ R ;
                  Now += (Vis[A[R]] << 1) ;
                  ++ Vis[A[R]] ;
              }
      //      cout << L <<' '<<R <<' '<<Now<<endl;
              while (R > Q[i].Right ) {
                  -- Vis[A[R]] ;
                  Now -= (Vis[A[R]] << 1) ;
                  -- R ;
              }
      //      cout << L <<' '<<R <<' '<<Now<<endl;
              Ans[Q[i].Place] = Now , Sum[Q[i].Place ] = ((long long)Q[i].Right - Q[i].Left ) * (Q[i].Right - Q[i].Left + 1) ;
          }
          std :: sort (Q + 1 , Q + 1 + M , Cmp2) ;
          for (int i = 1 ; i <= M; ++ i) {
              if (Q[i].Left == Q[i].Right ) printf ("0/1
      ") ;
              else {
                  int K = GCD (Ans[i] , Sum[i]) ;
                  Ans[i] /= K , Sum[i] /= K ;
                  printf ("%lld/%lld
      " , Ans[i] , Sum[i]) ;
              }
          }
          fclose (stdin) , fclose (stdout) ;
          return 0 ;
      }
      

Thanks!

原文地址:https://www.cnblogs.com/bj2002/p/10791896.html