您的位置:首页 > 其它

NOIP模拟(11.03)T1 区间

2017-11-03 20:47 393 查看
区间
题目背景:
11.03 NOIP模拟T1
分析:暴力
 
被今天的题毒瘤到了······T1全场一个AC,两个60分,其他全部是30及以下······
直接讲题,首先比较显然的,对于两个给定的数字,我们直接将1 ~ i当中两者出现次数之差算出来,那么前面出现同样的差值的位置到i就应该是一个合法的区间,这样做的话,我们的复杂度是nq,因为每一次询问都要扫一遍,考虑60分档当中说的,不同数字只会有最多50个,那么相当于只有2500组询问我们直接将所有的答案预处理出来就可以了。
Source:
/*
created by scarlyw
*/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
#include <ctime>

const int MAXN = 8000 + 10;

int n, q, x, y, top;
int c[MAXN], rk[MAXN];
int cnt[MAXN][MAXN], ans[MAXN][MAXN];

struct data {
int num, ori;
inline bool operator < (const data &a) const {
return num < a.num;
}
} a[MAXN];

inline void read_in() {
std::cin >> n >> q;
for (int i = 1; i <= n; ++i) std::cin >> a[i].num, a[i].ori = i;
}

inline void pre_work() {
std::sort(a + 1, a + n + 1);
int i = 1;
while (i <= n) {
int j = i;
while (a[i].num == a[i + 1].num) {
if (i == n) break ;
++i;
}
++top, rk[top] = a[i].num;
for (int k = j; k <= i; ++k) c[a[k].ori] = top;
++i;
}
static int temp[MAXN * 2];
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= top; ++j) cnt[i][j] = cnt[i - 1][j];
cnt[i][c[i]]++;
}
for (int i = 0; i <= top; ++i)
for (int j = 0; j <= top; ++j) {
memset(temp, 0, sizeof(int) * (n + 1 + n));
for (int k = 0; k <= n; ++k) {
ans[i][j] += temp[cnt[k][i] - cnt[k][j] + n];
temp[cnt[k][i] - cnt[k][j] + n]++;
}
}
}

inline int binary(int x) {
int l = 0, r = top + 1;
while (l + 1 < r) {
int mid = l + r >> 1;
rk[mid] <= x ? l = mid : r = mid;
}
return (rk[l] == x) ? (l) : 0;
}

inline void solve() {
while (q--)
std::cin >> x >> y, std::cout << ans[binary(x)][binary(y)] << '\n';
}

int main() {
// freopen("interval.in", "r", stdin);
// freopen("interval.out", "w", stdout );
std::ios::sync_with_stdio(NULL);
std::cin.tie(NULL), std::cout.tie(NULL);
read_in();
pre_work();
solve();
return 0;
}
考虑100分该怎么做,上一个算法的瓶颈在于,每一次都是O(n)的枚举,但是我们发现,如果当前位置上既不是x,也不是y的时候,其实它的贡献和上一个位置应该是相同的,那么我们可以考虑将贡献相同的位置直接算作一个位置,然后直接计算贡献之间和同一位置内部的贡献,这样的话,一次询问的复杂度就是两种数字出现的次数之和,考虑对于一个位置的枚举,显然只会在和其他的8000种颜色询问过程中被用到,那么也就是说最多只会被枚举8000次,那么总复杂度应该是O(n2)的。
注意:对于没有出现x, y的位置,应该与上一个出现这两个数字的位置等价,分段的时候内部的贡献应该为cnt
* (cnt - 1) / 2,并且每次加上当前应该加上cnt而不是加1,边界有点恶心。
Source:
/*
created by scarlyw
*/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
#include <ctime>

const int MAXN = 8000 + 10;
const int INF = 1000000000;

int n, q, x, y, top, ans;
int c[MAXN], rk[MAXN];
std::vector<int> pos[MAXN];

struct data {
int num, ori;
inline bool operator < (const data &a) const {
return num < a.num;
}
} a[MAXN];

inline void read_in() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i].num), a[i].ori = i;
}

inline void pre_work() {
std::sort(a + 1, a + n + 1);
int i = 1;
while (i <= n) {
int j = i;
while (a[i].num == a[i + 1].num) {
if (i == n) break ;
++i;
}
++top, rk[top] = a[i].num;
for (int k = j; k <= i; ++k) c[a[k].ori] = top;
++i;
}
for (int i = 1; i <= n; ++i) pos[c[i]].push_back(i);
for (int i = 0; i <= top; ++i) pos[i].push_back(n + 1);
}

inline int binary(int x) {
int l = 0, r = top + 1;
while (l + 1 < r) {
int mid = l + r >> 1;
rk[mid] <= x ? l = mid : r = mid;
}
return (rk[l] == x) ? (l) : 0;
}

inline int calc(int x) {
return (x == 0) ? (0) : (x * (x - 1) / 2);
}

std::vector<int> p;
inline void solve() {
while (q--) {
scanf("%d%d", &x, &y), x = binary(x), y = binary(y), ans = 0;
int h1 = 0, h2 = 0, last = 0, cur_sum = 0;
static int temp[MAXN * 2];
temp
++;
while (last != n) {
if (pos[x][h1] < pos[y][h2]) {
int len = pos[x][h1] - 1 - last;
ans += len * temp[cur_sum + n];
ans += calc(len), temp[cur_sum + n] += len;
last = pos[x][h1] - 1, ++h1, p.push_back(cur_sum + n);
cur_sum++;
} else {
int len = pos[y][h2] - 1 - last;
ans += len * temp[cur_sum + n];
ans += calc(len), temp[cur_sum + n] += len;
last = pos[y][h2] - 1, ++h2, p.push_back(cur_sum + n);
cur_sum--;
}
}
while (!p.empty()) temp[p.back()] = 0, p.pop_back();
std::cout << ans << '\n';
}
}

int main() {
// freopen("interval.in", "r", stdin);
// freopen("interval.out", "w", stdout);
read_in();
pre_work();
solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: