zoto【来源:2021杭电多校第一场的第10题】
题目:
Problem Description
You are given an array fx.
For each i(1<=i<=n) , we use a point (i,fx[i]) in XoY coordinate plane to described it.
You are asked to answer m queries, in each query there will be a rectangle and you need to count how many different y-cooordinate (of the points mentioned above) in the queried rectangle.
Input
The first line contains an integer T(1<=T<=5) representing the number of test cases.
For each test case , there are two integers n,m(1<=n<=100000,1<=m<=100000) in the first line.
Then one line contains n integers fx[i](0<=fx[i]<=100000)
Each of the next m lines contain four integers x0,y0,x1,y1(1<=x0<=x1<=n,0<=y0<=y1<=100000) which means matrix's lower-leftmost cell is (x0,y0) and upper-rightest cell is (x1,y1).
Output
For each test case print a single integer in a new line.
Sample Input
14 2 1 0 3 1 1 0 4 3 1 0 4 2
Sample Output
32
Source
分析:
> 题目:
> T组测试数据, T大小为5:
> 给定n个坐标,横坐标为从1到n,对应纵坐标为fx[i],共有m此询问,其中n,m,fx大小都是1e5
> 每次询问,查询一个以(x0,y0)为左下角,以(x1,y1)为右上角的矩形面积内,有多少个不同的fx
> 解决:
> T是T,m是1e5,那么,我们每次的查询操作的时间复杂度需要降到O(logn)才能合法,而且,只有查询没有修改,基础莫队便可以解决次题。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, m, len;
int w[N], cnt[N], sum[N], ans[N];
struct Query{
int l, r, L, R, id;
}q[N];
int get(int x)
{
return x / len;
}
bool cmp(const Query& a, const Query& b)
{
int x = get(a.l), y = get(b.l);
if (x != y) return x < y;
return a.r < b.r;
// if (x == y) return a.r < b.r;
// return (x & 1) ? x > y : x < y;
}
void add(int x)
{
// printf("del: x = %d, cnt[x] = %d\n", x, cnt[x]);
//sum计算块内的节点数量,我们对y轴分块,计算的是fx的分块内的值
if (!cnt[x]) sum[get(x)] ++;
cnt[x] ++;
}
void del(int x)
{
// printf("del: x = %d, cnt[x] = %d\n", x, cnt[x]);
cnt[x] --;
if (!cnt[x]) sum[get(x)] --;
}
int calc(int y)
{
//计算y轴,从0到y的个数
int res = 0;
//sum代表y轴分块val的数量,res先求的是前缀和
for (int i = 0; i < get(y); i ++ ) res += sum[i];
//最上面的一点,这些不再整块内,暴力计算
for (int i = get(y) * len; i <= y; i ++ ) res += (cnt[i] >= 1);
return res;
}
void solve()
{
memset(cnt, 0, sizeof cnt);
memset(sum, 0, sizeof sum);
scanf("%d%d", &n, &m);
// len = sqrt(n);
len = 131;
for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
for (int i = 0; i < m; i ++ )
{
int l, L, r, R; scanf("%d%d%d%d", &l, &L, &r, &R);
q[i] = {l, r, L, R, i};
}
sort(q, q + m, cmp);
for (int i = 0, l = 1, r = 0; i < m; i ++ )
{
// printf("q[i].l = %d, bottom = %d, q[i].r = %d, top = %d\n", q[i].l, q[i].L, q[i].r, q[i].R);
// printf("del l ++ \n");
while (l < q[i].l) del(w[l ++ ]);
// printf("add -- l \n");
while (l > q[i].l) add(w[-- l ]);
// printf("add ++ r \n");
while (r < q[i].r) add(w[++ r ]);
// printf("del r -- \n");
while (r > q[i].r) del(w[r -- ]);
ans[q[i].id] = calc(q[i].R) - calc(q[i].L - 1);
}
for (int i = 0; i < m; i ++ ) printf("%d\n", ans[i]);
}
int main()
{
int T = 1;
cin >> T;
while (T -- )
{
solve();
}
return 0;
}
题目:
HH 有一串由各种漂亮的贝壳组成的项链。
HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。
HH 不断地收集新的贝壳,因此他的项链变得越来越长。
有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?
这个问题很难回答,因为项链实在是太长了。
于是,他只好求助睿智的你,来解决这个问题。
输入格式
第一行:一个整数 NN,表示项链的长度。
第二行:NN 个整数,表示依次表示项链中贝壳的编号(编号为 00 到 10000001000000 之间的整数)。
第三行:一个整数 MM,表示 HH 询问的个数。
接下来 MM 行:每行两个整数,LL 和 RR,表示询问的区间。
输出格式
MM 行,每行一个整数,依次表示询问对应的答案。
数据范围
1≤N≤500001≤N≤50000,
1≤M≤2×1051≤M≤2×105,
1≤L≤R≤N1≤L≤R≤N输入样例:
6
1 2 3 4 3 5
3
1 2
3 5
2 6
输出样例:
2
2
4
分析:
题目:
给定一个长度为n的序列w,n长度为5e4,w大小为5e4,对此序列进行m次查询,m大小为2e5,每次查询询问一个区间内有多少个不同的数字。
解决:
这时离线询问操作,可以使用基础莫队解决此题。时间复杂度为O(nsqrt(n))
莫队算法将
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 50005, M = 200005, S = 1000010;
int n, m, T, a[N];
struct Q{
int l, r, x, y, i;
inline Q() {}
inline Q(int l, int r, int i) : l(l), r(r), i(i) {x = l / T, y = r;}
inline friend bool operator< (const Q &a, const Q &b){
return a.x == b.x ? (a.x & 1) ? a.y < b.y : a.y > b.y : a.x < b.x;
}
}q[M];
int ans[M], cnt[S];
void add(int x, int& now)
{
if (!cnt[x]) now ++;
cnt[x] ++;
}
void del(int x, int& now)
{
cnt[x] --;
if (!cnt[x]) now --;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
scanf("%d", &m);
T = sqrt((double)n * n / m);
for (int i = 0; i < m; i ++ )
{
int l, r; scanf("%d%d", &l, &r);
q[i] = {l, r, i};
}
sort(q, q + m);
for (int i = 0; i < m; i ++ )
printf("[l = %d, r = %d]\n", q[i].l, q[i].r);
for (int i = 0, l = 1, r = 0, now = 0; i < m; i ++ )
{
while (l > q[i].l) add(a[ -- l], now);
while (l < q[i].l) del(a[ l ++], now);
while (r > q[i].r) del(a[ r --], now);
while (r < q[i].r) add(a[ ++ i], now);
ans[q[i].i] = now;
}
for (int i = 0; i < m; i ++ ) printf("%d\n", ans[i]);
return 0;
}
[P1494 国家集训队]小Z的袜子
题目:
题目描述
upd on 2020.6.10 :更新了时限。
作为一个生活散漫的人,小 Z 每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小 Z 再也无法忍受这恼人的找袜子过程,于是他决定听天由命……
具体来说,小 Z 把这 NN 只袜子从 11 到 NN 编号,然后从编号 LL 到 RR (LL 尽管小 Z 并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。
你的任务便是告诉小 Z,他有多大的概率抽到两只颜色相同的袜子。当然,小 Z 希望这个概率尽量高,所以他可能会询问多个 (L,R)(L,R) 以方便自己选择。
然而数据中有 L=RL=R 的情况,请特判这种情况,输出
0/1
。输入格式
输入文件第一行包含两个正整数 NN 和 MM。NN 为袜子的数量,MM 为小 Z 所提的询问的数量。接下来一行包含 NN 个正整数 C_iCi,其中 C_iCi 表示第 ii 只袜子的颜色,相同的颜色用相同的数字表示。再接下来 MM 行,每行两个正整数 L, RL,R 表示一个询问。
输出格式
包含 MM 行,对于每个询问在一行中输出分数 A/BA/B 表示从该询问的区间 [L,R][L,R] 中随机抽出两只袜子颜色相同的概率。若该概率为 00 则输出
0/1
,否则输出的 A/BA/B 必须为最简分数。(详见样例)输入输出样例
输入 #1复制
6 4
1 2 3 3 3 2
2 6
1 3
3 5
1 6
输出 #1复制
2/5
0/1
1/1
4/15
说明/提示
30% 的数据中,N,M≤5000;
60% 的数据中,N,M≤25000;
100% 的数据中,N,M≤50000,1≤L<R≤N,Ci≤N。
分析:
题目:
给定长度为n的序列w,其中n和w都是50000,给定m次询问,每次询问查找区间内,有两个相同的数字的概率为多少。
解决:
基础莫队。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 50005;
int n, m, len;
int w[N];
int cnt[N];
struct Query{
int l, r, id;
}q[N];
struct Answer{
int x, y;
}ans[N], res;
inline int rd()
{
int x = 0, f = 0; char c = getchar();
while (!isdigit(c)) if (c == '-') f = 1, c = getchar();
while (isdigit(c)) x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return f ? -x : x;
}
inline int get(int x)
{
return x / len;
}
inline bool cmp(const Query& a, const Query& b)
{
int x = get(a.l), y = get(b.l);
if (x == y) return (x & 1) ? a.r < b.r : a.r > b.r;
return x < y;
}
inline void add(int x)
{
cnt[x] ++;
if (cnt[x] > 1) res.x += cnt[x] * (cnt[x] - 1) - (cnt[x] - 1) * (cnt[x] - 2);
}
inline void del(int x)
{
cnt[x] --;
if (cnt[x] > 0) res.x -= cnt[x] * (cnt[x] + 1) - cnt[x] * (cnt[x] - 1);
}
inline int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a % b);
}
signed main()
{
// n = rd(), m = rd();
scanf("%lld%lld", &n, &m);
len = sqrt(n);
for (int i = 1; i <= n; i ++ ) scanf("%lld", &w[i]);
// w[i] = rd();
for (int i = 0; i < m; i ++ )
{
int l, r;
scanf("%lld%lld", &l, &r);
// l = rd(), r = rd();
q[i] = {l, r, i};
}
sort(q, q + m, cmp);
// cout << "-------------" << endl;
for (int i = 0, l = 1, r = 0; i < m; i ++ )
{
if (q[i].l == q[i].r)
{
ans[q[i].id] = {0, 1};
continue;
}
while (l < q[i].l) del(w[l ++ ]);
while (l > q[i].l) add(w[-- l ]);
while (r < q[i].r) add(w[++ r ]);
while (r > q[i].r) del(w[r -- ]);
res.y = (q[i].r - q[i].l + 1) * (q[i].r - q[i].l);
// printf("%lld\n", res.y);
int x = res.x, y = res.y;
int d = gcd(x, y);
ans[q[i].id] = {x / d, y / d};
// printf("x = %lld, y = %lld, gcd = %lld\n", x, y, d);
}
for (int i = 0; i < m; i ++ ) printf("%lld/%lld\n", ans[i].x, ans[i].y);
return 0;
}