题目链接:
https://www.lydsy.com/JudgeOnline/problem.php?id=2038/B
题意:
给你一个序列 a a a,每个不同 a [ i ] a[i] a[i]代表一种颜色,并给你一些查询 [ l , r ] [l,r] [l,r],问你在这个区间里随机选取两个元素,它们颜色相同的概率,以最简分数输出,若为0则以0/1形式输出;
分析:
首先需要两个变量分别维护分子和分母,不然没法使用莫队进行所谓的 O ( 1 ) O(1) O(1)的转化;那么 a n s 1 ans1 ans1记录分子, a n s 2 ans2 ans2记录分母, a n s 2 ans2 ans2很好求,对于每个区间 [ l , r ] [l,r] [l,r]来说可以直接求,即 a n s 2 = C r − l + 1 2 = ( r − l + 1 ) ∗ ( r − l ) / 2 ans2=C_{r-l+1}^2=(r-l+1)*(r-l)/2 ans2=Cr−l+12=(r−l+1)∗(r−l)/2,对于分子,如果是缩小区间(即指针向内靠近)则分子的一部分(因为可能有很多种颜色,这里只考虑指针移动位置的颜色的选择)由 C c o t [ a [ x ] ] 2 C_{cot[a[x]]}^2 Ccot[a[x]]2变成 C c o t [ a [ x ] ] − 1 2 C_{cot[a[x]]-1}^2 Ccot[a[x]]−12,那么这个时候, a n s 1 ans1 ans1则是减少了 c o t [ a [ x ] ] − 1 cot[a[x]]-1 cot[a[x]]−1,即 a n s 1 − = ( c o t [ a [ x ] ] − 1 ) ans1-=(cot[a[x]]-1) ans1−=(cot[a[x]]−1),若区间指针向外扩展,则有 C c o t [ a [ x ] ] 2 C_{cot[a[x]]}^2 Ccot[a[x]]2变成 C c o t [ a [ x ] ] + 1 2 C_{cot[a[x]]+1}^2 Ccot[a[x]]+12,这个时候有 a n s 1 + = c o t [ a [ x ] ] ans1+=cot[a[x]] ans1+=cot[a[x]],这即为两个关键操作,其中我也考虑过 c o t [ a [ x ] ] < 2 cot[a[x]]<2 cot[a[x]]<2的情况,然后发现想太多了,这种情况同样符合上述考虑;还有就是有几个位置注意爆int的问题,开long long;
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define inf 0x7f7f7f7f
#define maxn 50009
#define N 2
#define mod 100003
typedef long long ll;
typedef struct {
int u, v, next, w;
} Edge;
Edge e[N];
int cnt, head[N];
inline void add1(int u, int v) {
e[cnt].u = u;
e[cnt].v = v;
//e[cnt].w=w;
// e[cnt].f=f;
e[cnt].next = head[u];
head[u] = cnt++;
//e[cnt].u=v;
// e[cnt].v=u;
// e[cnt].w=0;
// e[cnt].f=-f;
// e[cnt].next=head[v];
// head[v]=cnt++;
}
inline void write(int x) {
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
inline int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
struct node {
int l, r, id;
} p[maxn];
ll num, ans1, ans2, n, m, c[maxn], pos[maxn], cot[maxn], top[maxn], bot[maxn], ans[maxn][2];
bool com(node a, node b) {
if (pos[a.l] != pos[b.l])return a.r < b.r;
else return pos[a.l] < pos[b.l];
}
inline void del(int x) {
cot[c[x]]--;
ans1 -= cot[c[x]];
}
inline void add(int x) {
ans1 += cot[c[x]];
cot[c[x]]++;
}
inline ll _gcd(int a, int b) {
return b == 0 ? a : _gcd(1ll * b, 1ll * a % b);
}
int main() {
cin >> n >> m;
int sz = sqrt(n);
for (int i = 1; i <= n; i++) {
scanf("%d", &c[i]);
}
for (int i = 1; i <= m; i++) {
scanf("%d%d", &p[i].l, &p[i].r);
pos[i] = i / sz;
p[i].id = i;
}
int l = 1, r = 0;
sort(p + 1, p + 1 + m, com);
for (int i = 1; i <= m; i++) {
while (l < p[i].l) {
del(l);
l++;
}
while (l > p[i].l) {
l--;
add(l);
}
while (r < p[i].r) {
r++;
add(r);
}
while (r > p[i].r) {
del(r);
r--;
}
ans2 = 1ll * (r - l + 1) * (r - l) / 2;
if (ans1 == 0) {
top[p[i].id] = 0, bot[p[i].id] = 1;
continue;
}
//cout<<ans1<<" "<<ans2<<endl;
ll gcd = _gcd(ans1, ans2);
top[p[i].id] = ans1 / gcd;
bot[p[i].id] = ans2 / gcd;
}
for (int i = 1; i <= m; i++)
cout << top[i] << "/" << bot[i] << endl;
return 0;
}
我们坚持一件事情,并不是因为这样做了会有效果,而是坚信,这样做是对的。
——哈维尔