小z的袜子 莫队算法

  1 /*
  2   题意:n,m<=50000 一个长度为n的区间,每个位置有一个颜色,查询一个区间[l,r]中取两个点颜色相同的概率。
  3   题解:莫队算法, 统计当前区间每种颜色的个数.
  4   时间:2018.07.20
  5 */
  6 
  7 #include <bits/stdc++.h>
  8 using namespace std;
  9 
 10 typedef long long LL;
 11 const int MAXN = 100005;
 12 const LL MOD7 = 1e9+7;
 13 
 14 struct Node
 15 {
 16     int l,r;
 17     int idx;
 18 }Q[MAXN];
 19 int belong[MAXN],qsize; // 分块
 20 int a[MAXN];  // 颜色
 21 LL flag[MAXN];  // 目前维护的区间中各种颜色的个数
 22 LL ans[MAXN];  // 询问的答案
 23 LL len[MAXN];   // 询问区间的长度
 24 LL Ans;         //  当前区间内的答案
 25 int n,m;
 26 
 27 
 28 // 按照分块的位置对所有的询问排序
 29 int cmp(Node na,Node nb)
 30 {
 31     if (belong[na.l]==belong[nb.l])
 32         return na.r<nb.r;
 33     return belong[na.l]<belong[nb.l];
 34 }
 35 
 36 // 确定分块的位置
 37 void build()
 38 {
 39     qsize = sqrt(n);
 40     for (int i=1;i<=n;++i) belong[i] = (i-1)/qsize+1;
 41 }
 42 
 43 
 44 // 当前区间扩展一个点x,考虑x的颜色a[x],那么当前点对区间的贡献应该为区间中颜色为a[x]的个数即flag[a[x]]
 45 void Add(int x)
 46 {
 47     Ans+=flag[a[x]];
 48     ++flag[a[x]];
 49 }
 50 // 删除一个端点, 那么先把当前的颜色删除,然后删除当前颜色对区间的贡献
 51 void Delete(int x)
 52 {
 53     --flag[a[x]];
 54     Ans-=flag[a[x]];
 55 }
 56 
 57 LL gcd(LL a, LL b)
 58 {
 59     if (b==0) return a;
 60     return gcd(b,a%b);
 61 }
 62 
 63 int main()
 64 {
 65 #ifndef ONLINE_JUDGE
 66     freopen("test.txt","r",stdin);
 67 #endif // ONLINE_JUDGE
 68     scanf("%d%d",&n,&m);
 69     for (int i=1;i<=n;++i) scanf("%d",&a[i]);
 70     for (int i=1;i<=m;++i)
 71     {
 72         scanf("%d%d",&Q[i].l,&Q[i].r);
 73         Q[i].idx=i;
 74         len[i] = Q[i].r-Q[i].l+1;
 75     }
 76     build();
 77     sort(Q+1,Q+1+m,cmp);
 78     memset(flag,0LL,sizeof(flag));
 79     Ans=0;
 80     int l=1,r=0;
 81     for (int i=1;i<=m;++i)
 82     {
 83         int idx=Q[i].idx;
 84         // 查询的区间小于当前的区间,那么左端点右移,并且移动前删除端点的贡献
 85         while (l<Q[i].l) {
 86             Delete(l);
 87             ++l;
 88         }
 89         // 查询的区间大于当前的区间,那么假如下面一个端点的贡献,左移区间端点
 90         while (l>Q[i].l) {
 91             Add(l-1);
 92             --l;
 93         }
 94         //
 95         while (r<Q[i].r) {
 96             Add(r+1);
 97             ++r;
 98         }
 99         while (r>Q[i].r) {
100             Delete(r);
101             --r;
102         }
103         ans[idx]=Ans;
104     }
105     for (int i=1;i<=m;++i)
106     {
107         LL tmp = len[i]*(len[i]-1);
108         LL d=gcd(2*ans[i],tmp);
109         // printf("%lld\n",2*ans[i]);
110         printf("%lld/%lld\n",2*ans[i]/d, tmp/d);
111     }
112     return 0;
113 }

 

转载于:https://www.cnblogs.com/LeeSongt/p/9340395.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值